實(shí)現(xiàn)一個(gè)方法的鏈?zhǔn)秸{(diào)用
題目描述
請(qǐng)使用 TypeScript 實(shí)現(xiàn)一個(gè)支持鏈?zhǔn)秸{(diào)用的 Cat 類,要求如下:
cat.say("Tom")
:立即輸出hello Tom
到控制臺(tái);cat.sleep(3000)
:等待 3000 毫秒后再繼續(xù)執(zhí)行后續(xù)任務(wù);cat.bye("Bye")
:輸出 Bye 到控制臺(tái)。
調(diào)用示例:
const cat = new Cat();
cat.say("Tom").sleep(3000).bye("Bye");
期望輸出:
hello Tom
# 等待 3 秒...
Bye
題目解析
這個(gè)需求是一個(gè)典型的 異步鏈?zhǔn)秸{(diào)用 場景,尤其是 sleep
延遲后再繼續(xù)執(zhí)行后續(xù)操作。實(shí)現(xiàn)時(shí)關(guān)鍵在于:
- 每次調(diào)用都注冊(cè)一個(gè)異步任務(wù);
- 按順序排隊(duì)執(zhí)行(使用
Promise
鏈或任務(wù)隊(duì)列); - 每個(gè)方法返回
this
以保持鏈?zhǔn)秸{(diào)用。
代碼實(shí)現(xiàn)
class Cat {
private taskQueue: (() => Promise<void>)[] = [];
constructor() {
// 自動(dòng)開始執(zhí)行任務(wù)隊(duì)列
Promise.resolve().then(() => this.runQueue());
}
private async runQueue() {
for (const task of this.taskQueue) {
await task();
}
}
say(name: string): this {
this.taskQueue.push(() => {
return new Promise<void>((resolve) => {
console.log("hello", name);
resolve();
});
});
return this;
}
sleep(ms: number): this {
this.taskQueue.push(() => {
return new Promise<void>((resolve) => {
setTimeout(resolve, ms);
});
});
return this;
}
bye(msg: string): this {
this.taskQueue.push(() => {
return new Promise<void>((resolve) => {
console.log(msg);
resolve();
});
});
return this;
}
}
// 使用方式
const cat = new Cat();
cat.say("Tom").sleep(3000).bye("Bye");
說明:
- taskQueue 是一個(gè)異步任務(wù)列表。
- 每個(gè)方法通過 this.taskQueue.push(() => Promise) 添加一個(gè)任務(wù)。
- 構(gòu)造函數(shù)中立即開始異步執(zhí)行任務(wù)隊(duì)列。
- sleep 實(shí)現(xiàn)延遲,后續(xù)任務(wù)自然排隊(duì)執(zhí)行。
拓展
如果要進(jìn)行逆向鏈?zhǔn)秸{(diào)用,如何實(shí)現(xiàn)呢?
cat.bye("Bye").sleep(2000).say("Say");
執(zhí)行順序:
Say
# 等待 2 秒...
Bye
所有方法仍然鏈?zhǔn)秸{(diào)用,但執(zhí)行順序以調(diào)用順序?yàn)闇?zhǔn),同時(shí) sleep 是延遲“下一個(gè)任務(wù)”的執(zhí)行,而不是“當(dāng)前任務(wù)”的延遲。
class Cat {
private queue: { fn: () => void; delay: number }[] = [];
private nextDelay = 0;
constructor() {
Promise.resolve().then(() => this.run());
}
private async run() {
for (const task of this.queue) {
if (task.delay > 0) {
await new Promise((res) => setTimeout(res, task.delay));
}
task.fn();
}
}
private addTask(fn: () => void): this {
this.queue.push({ fn, delay: this.nextDelay });
this.nextDelay = 0; // reset after applying
return this;
}
say(msg: string): this {
return this.addTask(() => {
console.log(msg);
});
}
bye(msg: string): this {
return this.addTask(() => {
console.log(msg);
});
}
sleep(ms: number): this {
this.nextDelay = ms; // apply to next task
return this;
}
}