刷到一篇面经: 一个半小时的腾讯一面 - 掘金
里面的第一题,实现一个HardMan函数,实现链式调用。
初次查看,以为用 async 函数可以轻松拿下,但尝试后发现 async 函数返回的还是Promise,直接链式调用必然会用到 then 。
经过一段时间学习后,总结了2种实现方法:
- 使用内置异步链
- 使用异步任务队列
1. 内置异步链
利用 Promise.resolve().then 实现阻塞
function HardMan(name) {
console.log(`I am ${name}`);
let lastTime = new Date();
let promiseChain = Promise.resolve(); // 用于管理异步链
return {
rest(seconds) {
promiseChain = promiseChain.then(() => {
console.log(`(等待 ${seconds} 秒)`);
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, seconds * 1000);
});
});
return this; // 保持链式调用
},
learn(subject) {
promiseChain = promiseChain.then(() => {
const now = new Date();
const timeDiff = Math.round((now - lastTime) / 1000);
lastTime = now;
console.log(`Start learning after ${timeDiff} seconds`);
console.log(`Learning ${subject}`);
});
return this; // 保持链式调用
},
};
}
HardMan("Jack").rest(3).learn("JavaScript").rest(2).learn("Vue");
2.异步任务队列
利用 setTimeout((),0) 的特性,在所有同步任务完成后才启动。那么就可以把所有异步 Promise 推入任务队列后再执行。
function HardMan(name) {
console.log(`I am ${name}`);
let lastTime = Date.now();
const tasks = []; // 任务队列
const obj = {
rest: function (seconds) {
console.log("推入1");
tasks.push(() => {
console.log(`(等待 ${seconds} 秒)`);
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, seconds * 1000);
});
});
return obj;
},
learn: function (subject) {
console.log("推入2");
tasks.push(() => {
const curTime = Date.now();
const timeDiff = Math.round((curTime - lastTime) / 1000);
lastTime = Date.now();
console.log(`Start learning after ${timeDiff} seconds`);
console.log(`Learning ${subject}`);
});
return obj;
},
};
// 任务执行器
console.log("任务执行器触发");
setTimeout(async () => {
for (const task of tasks) {
await task();
}
}, 0);
return obj;
}
HardMan("Jack")
.learn("computer")
.rest(1)
.learn("computer")
.rest(1)
.learn("computer");