在node.js后端开发需求中,很容易遇到对一些列表做异步控制的行为。比如对一个订单列表依次做数据库更新。对一个工单列表请求外部关联的数据。
类似用继发(for of,for await of)或并发(Promise.all, Promise.allSettled)的方式来做,或多或少都有些不足。继发不能充分利用系统资源,整体来说较慢。并发又担心失败率或给到较大负载。 这种情况下,离不开并发控制。
如下为具体实现:
/**
* @desc the method of concurrency control
* @param {Array<any>} array
* @param {function} iterator
* @param {number} concurrency - number of concurrent tasks
*/
async function asyncConcurrent(array, iterator, concurrency = 5) {
const allAsyncTasks = [];
const executingAsyncTasks = [];
const executeResult = {};
for (const [index, item] of array.entries()) {
const asyncTask = Promise.resolve()
.then(() => iterator(item, index, array))
.then((value) => {
executeResult[index] = { status: 'fulfilled', value };
})
.catch((error) => {
executeResult[index] = { status: 'rejected', error };
})
.finally(() => {
executingAsyncTasks.splice(executingAsyncTasks.indexOf(asyncTask), 1);
});
allAsyncTasks.push(asyncTask);
if (concurrency <= array.length) {
executingAsyncTasks.push(asyncTask);
if (executingAsyncTasks.length >= concurrency) {
await Promise.race(executingAsyncTasks);
}
}
}
await Promise.all(allAsyncTasks);
return array.map((_, index) => executeResult[index]);
}
返回结果仿照了Promise.allSettled, 对于给定的任务列表,让其全部执行完再返回, 记录下失败的错误信息,和成功执行的返回值。