并发调度与超时
如何等一批异步任务全部完成?
- 组合:
Array.map生成 Promise 数组, 再用await Promise.all等待全部完成;
const promiseList = items.map(async (item) => doWork(item));
await Promise.all(promiseList);
如何实现 Promise 超时控制?
- 思路: 构造一个到点即拒绝的计时 Promise, 与原 Promise 一起交给
Promise.race;
const withTimeout = (promise, timeout) =>
Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error(`Promise timed out after ${timeout} ms`)), timeout),
),
]);
- 结果: 超时先到则整体拒绝, 原 Promise 仍在后台继续运行, 见 Promise 基础与静态方法;
如何实现并发调度器?
- 目标: 限制同时运行的任务数, 保持结果与输入顺序一致;
- 静态版: 一次性接收任务数组, 维护
runningSize、finishSize与待执行队列;
const createSchedule = (requestList, max) => {
let runningSize = 0;
let finishSize = 0;
const queue = requestList.map((task, index) => [task, index]);
const res = new Array(requestList.length);
return new Promise((resolve) => {
const handle = () => {
if (finishSize === requestList.length) resolve(res);
if (runningSize >= max || queue.length === 0) return;
const [task, index] = queue.shift();
task()
.then(
(value) => (res[index] = value),
(reason) => (res[index] = reason),
)
.finally(() => {
runningSize--;
finishSize++;
handle();
});
runningSize++;
};
for (let i = 0; i < requestList.length; i++) handle();
});
};
- 动态版: 返回一个接收任务的函数, 每次调用把任务与其
resolve/reject入队, 有空位就出队执行;
const createLimiter = (max) => {
const queue = [];
let running = 0;
const handle = () => {
if (queue.length === 0 || running >= max) return;
const { task, resolve, reject } = queue.shift();
task()
.then(resolve, reject)
.finally(() => {
running--;
handle();
});
running++;
};
return (task) =>
new Promise((resolve, reject) => {
queue.push({ task, resolve, reject });
handle();
});
};
循环中如何正确使用 await?
forEach: 回调是异步函数,await不会阻塞外层循环, 也无法等待完成;- 可行写法: 用
for...of串行等待, 或map收集 Promise 后统一等待; - 建议: 不在
filter、reduce中使用await;
for (const file of files) {
const contents = await fs.readFile(file, "utf8");
console.log(contents);
}
如何实现 sleep 并判断 async 函数?
const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay));
await sleep(1500);
- 判断方式: 读取构造函数名、
Object.prototype.toString标记, 或判断返回值是否为 Promise;
fn.constructor.name === "AsyncFunction";
Object.prototype.toString.call(async function () {}); // "[object AsyncFunction]"
如何实现可链式调用的延迟队列?
- 思路: 用一个
Promise.resolve()作为队列起点, 每次操作把新任务then到队尾, 从而串行执行并保持返回值可继续链式调用;
class Person {
constructor() {
this.queue = Promise.resolve();
}
eat() {
this.queue = this.queue.then(() => console.log("eat"));
return this;
}
sleep(time) {
this.queue = this.queue.then(() => new Promise((resolve) => setTimeout(resolve, time)));
return this;
}
}