怎样实现有明确失败语义的异步并发池?
参考答案
限制并发需要接收尚未执行的任务函数,而不是已经开始的 Promise。下面的教学实现接收函数数组,最多同时等待 limit 个任务,结果按输入顺序排列。这里限制的是异步在途任务,不会让 JavaScript 计算自动多线程并行。
JavaScript
async function runPool(tasks, limit) {
if (!Array.isArray(tasks)) throw new TypeError('任务必须是数组');
if (!Number.isInteger(limit) || limit < 1) {
throw new RangeError('并发数必须为正整数');
}
const queue = Array.from(tasks);
if (queue.some(task => typeof task !== 'function')) {
throw new TypeError('每个任务都必须是函数');
}
const results = new Array(queue.length);
let next = 0;
let failed = false;
async function worker() {
while (!failed && next < queue.length) {
const index = next++;
try {
results[index] = await queue[index]();
} catch (error) {
failed = true; // 得知失败后停止领取新任务
throw error;
}
}
}
await Promise.all(Array.from(
{ length: Math.min(limit, queue.length) },
() => worker(),
));
return results;
}
同步抛错或异步拒绝都会使整体拒绝。得知失败前已经启动的任务仍会继续,整体拒绝不表示它们已经停止;需要取消时,应另行设计 AbortSignal 协作。示例不包含重试、超时、动态加任务或每秒限流,生产需求要分别定义这些策略。
代码为本项目教学实现,参考文档用于核对 Promise 语义,不是官方提供的并发池。