在node.js中实现并发控制

760 阅读1分钟

在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, 对于给定的任务列表,让其全部执行完再返回, 记录下失败的错误信息,和成功执行的返回值。