懒汉式——LazyMan(任务队列应用)
代码
/*** 懒汉式 LazyMan* @description* 注意:* 1. 在每次执行的任务里面调用next(任务按顺序执行),而不是添加任务后执行next(立即执行task,多个任务会同时触发)* 2. 使用setTimeout确保构造函数执行完再启动任务队列*/
class LazyMan {constructor(name) {this.name = name// 任务队列this.tasks = []const task = () => {console.log(`Hi I am ${name}`);this.next();}this.tasks.push(task)// 确保构造函数执行完再启动任务队列setTimeout(() => {this.next()}, 0)}// 启动一次任务队列(执行第一个任务)next() {const task = this.tasks.shift()if (task) task()}eat(food) {const task = () => {console.log(`Eat ${food}~`);this.next();}this.tasks.push(task)return this}sleep(seconds) {const task = () => {setTimeout(() => {console.log(`Wake up after ${seconds}s`);this.next();}, seconds * 1000)}this.tasks.push(task)return this}sleepFirst(seconds) {const task = () => {setTimeout(() => {console.log(`Wake up after ${seconds}s`);this.next();}, seconds * 1000)}this.tasks.unshift(task)return this}
}// new LazyMan("Hank").eat("dinner");
// Hi I am Hank
// Eat dinner~// new LazyMan("Hank").sleep(2).eat("dinner");
// Hi I am Hank
// (2秒后) Wake up after 2s
// Eat dinner~// new LazyMan("Hank").sleepFirst(2).eat("dinner");
// (2秒后) Wake up after 2s
// Hi I am Hank
// Eat dinner~
在每次执行的任务里面调用next,而不是添加任务后执行next
每次执行的任务里面调用 next,确保任务按顺序执行。
如果添加任务后就立刻执行next,会导致每一个加入的任务都立刻执行,违背了串行,类似于并行执行。
使用setTimeout确保构造函数执行完再启动任务队列
先看构造函数里如果 立刻调用 this.next()
会怎样
class LazyMan {constructor(name) {this.tasks = []const task = () => {console.log(`Hi I am ${name}`)this.next()}this.tasks.push(task)// ⚠️ 这里如果立刻调用 nextthis.next()}next() {const task = this.tasks.shift()if (task) task()}eat(food) {const task = () => {console.log(`Eat ${food}`)this.next()}this.tasks.push(task)return this}
}new LazyMan("Tom").eat("apple")
执行顺序
-
new LazyMan("Tom")
执行构造函数。tasks.push("Hi I am Tom")
- 立刻
next()
→ 执行 “Hi I am Tom”
-
构造函数还没结束,已经执行掉第一个任务了。
-
接着调用
.eat("apple")
,才把Eat apple
放到队列里。- 但是此时队列已经空了(第一个任务执行完时队列已经没东西了)。
- 所以
Eat apple
永远不会执行。
👉 结果:
Hi I am Tom
而不是期望的:
Hi I am Tom
Eat apple
这就是「来不及入队」的意思。
再看 setTimeout(..., 0)
的情况
class LazyMan {constructor(name) {this.tasks = []const task = () => {console.log(`Hi I am ${name}`)this.next()}this.tasks.push(task)// ✅ 延迟到构造函数执行完后才启动队列setTimeout(() => {this.next()}, 0)}next() {const task = this.tasks.shift()if (task) task()}eat(food) {const task = () => {console.log(`Eat ${food}`)this.next()}this.tasks.push(task)return this}
}new LazyMan("Tom").eat("apple")
执行顺序
-
new LazyMan("Tom")
执行构造函数。tasks.push("Hi I am Tom")
setTimeout(..., 0)
安排一个异步任务(等同步代码执行完再跑)。
-
构造函数执行完毕,继续执行
.eat("apple")
。tasks.push("Eat apple")
-
同步代码跑完,进入事件循环 → 执行
setTimeout
回调里的this.next()
。- 这时队列是:[
Hi I am Tom
,Eat apple
] - 所以会先执行
"Hi I am Tom"
,再执行"Eat apple"
。
- 这时队列是:[
👉 结果:
Hi I am Tom
Eat apple
完全符合预期。