实现一个类,其实例的方法可以链式调用,它有一个 sleep 方法,可以 sleep 一段时间后再后续调用,还有一个firstSleep,只能链式调用一次,优先级最高,优先sleep一段时间再执行后续代码。
案例:
const boy = new PlayBoy("coder");
boy
.sayHi()
.play("王者")
.firstSleep(3000)
.sleep(1000)
.play("原神")
.sleep(2000)
.play("明日方舟");
// 执行结果:
// firstSleep:3000
// 大家好我是coder
// 我在玩王者
// sleep:1000
// 我在玩原神
// sleep:2000
// 我在玩明日方舟
一看到链式调用就想到Promise或者async/await方向去了,思维定式,事实是思路走不通,而是要使用任务队列。
思路其实是用一个任务队列去收集所有链式调用的函数,这个收集的过程就是在实例化之后的链式调用过程,同时在构造函数中,开启一个宏任务事件,当同步代码(也就是子类的链式调用收集函数)执行完成之后,开始顺序执行队列收集的函数
实现:
class PlayBoy {
constructor(name) {
this.name = name;
this.queue = []; // 记录调用的函数,全部是同步收集然后依次执行
this.index = 0; // 执行函数的索引
this.firstSleepWatch = false; // 检查 firstSleep 调用次数,最多调用一次
this.init();
}
// 因为执行需要在函数收集之前,因此需要利用事件机制,先收集后执行
init() {
setTimeout(() => {
console.log("此时函数收集完了,开始调用任务队列中的函数了:", this.queue);
this.run();
}, 0);
}
run() {
const fn = this.queue[this.index++];
// 最后一个 fn 为 undefined, 因此需要做处理
fn && fn();
}
firstSleep(delay) {
if (this.firstSleepWatch) {
throw Error("Already declared firstSleep!!");
}
// 将该方法添加到队头
this.queue.unshift(() => {
setTimeout(() => {
console.log(`firstSleep:${delay}`);
this.run();
}, delay);
});
this.firstSleepWatch = true;
return this;
}
sleep(delay) {
this.queue.push(() => {
setTimeout(() => {
console.log(`sleep:${delay}`);
this.run();
}, delay);
});
// 返回当前的实例对象,以便后续链式调用
return this;
}
sayHi() {
this.queue.push(() => {
console.log(`大家好我是${this.name}`);
this.run();
});
return this;
}
play(game) {
this.queue.push(() => {
console.log(`我在玩${game}`);
this.run();
});
return this;
}
}
// 实例在链式调用之前会先收集所有的调用函数,按照顺序放入队列中,收集完成后顺序执行
const boy = new PlayBoy("coder");
boy
.sayHi()
.play("王者")
.firstSleep(3000)
.sleep(1000)
.play("原神")
.sleep(2000)
.play("明日方舟");