跳到主要内容

并发调度与超时

如何等一批异步任务全部完成?​

  • 组合: 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),
),
]);

如何实现并发调度器?​

  • 目标: 限制同时运行的任务数, 保持结果与输入顺序一致;
  • 静态版: 一次性接收任务数组, 维护 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;
}
}