代码
/**
* 懒汉式 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)
// ⚠️ 这里如果立刻调用 next
this.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
完全符合预期。