题目描述
person.talk('hello').sleep(3000).talk('world')
复制代码
实现一个类 Person
,其实例支持上面的链式调用:打印 hello,3 秒后再打印 world。
问题分析
链式调用,每个方法肯定需要返回当前实例,首先我们初始化代码如下:
class Person {
talk(str: string) {
return this;
}
sleep(milSec: number) {
return this;
}
}
const person = new Person();
person.talk("hello").sleep(3000).talk("world");
复制代码
再来逐个分析成员方法:
talk
: 简单,其实就是console.log
sleep
: 也很简单,一个setTimeout
完善方法实现得到下面的代码:
class Person {
talk(str: string) {
console.log(str);
return this;
}
sleep(milSec: number) {
setTimeout(() => {
console.log("get up");
}, milSec);
return this;
}
}
const person = new Person();
person.talk("hello").sleep(3000).talk("world");
复制代码
问题来了,由于 js 的事件循环问题,上述代码会马上依次输出 hello
与 world
,然后三秒后输出 get up
,如下所示:
hello
world
# 三秒后输出 get up
get up
复制代码
这个题目本质上就是一个任务调度的问题,既然是任务调度,那肯定就会联想到任务队列,思路就豁然开朗了:
- 成员方法不执行具体逻辑,而是将具体逻辑放到一个任务队列中,然后通知执行器去执行
- 定义执行器方法
run
,不断从任务队列中获取任务并且执行 - 有新的任务执行通知时,如果当前有任务正在执行或者队列中还有其他任务正在等待,阻塞当前任务,待清空队列之前的任务后再执行,否则直接执行该任务
完整代码如下所示:
type Task = () => Promise<boolean>;
class Person {
tasks: Task[] = [];
isRunning: boolean = false;
talk(str: string) {
const fn: Task = () => {
return new Promise((resolve) => {
console.log(str);
resolve(true);
});
};
this.tasks.push(fn);
this.run();
return this;
}
sleep(milSec: number) {
const fn: Task = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(true);
}, milSec);
});
};
this.tasks.push(fn);
this.run();
return this;
}
run() {
if (this.isRunning || !this.tasks.length) return;
this.isRunning = true;
// 从队列中取出第一个任务执行
const task = this.tasks.shift() as Task;
task().then(() => {
this.isRunning = false;
// 执行下一个任务
this.run();
});
}
}
const person = new Person();
person.talk("hello").sleep(3000).talk("world");
复制代码
至此,大功告成~
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END