Skip to content

根据 cpu 核心数让 promise.all 执行的更快

在 vue 的源码中看到一段代码,发出感叹,原来可以根据 cpu 核心数让 promise.all 执行的更快!

假设传入 promise.all 的数组参数的长度小于等于 cpu 核心数,那么不需要做额外的操作。

而如果大于 cpu 核心数,那么可以做一些优化操作。

js
async function buildAll(targets) {
  await runParallel(require('os').cpus().length, targets, build)
}

// 第一个参数:最大 cpu 核心数
// 第二个参数:执行的脚本任务数量
// 第三个参数:可调用函数
async function runParallel(maxConcurrency, source, iteratorFn) {
  const ret = []
  const executing = []
  // 循环脚本任务
  for (const item of source) {
    // 将执行脚本任务放入微任务队列中,返回一个 promise
    const p = Promise.resolve().then(() => iteratorFn(item, source))
    // 将 promise 放入 res 数组中
    ret.push(p)
    // 如果执行的脚本任务数量大于电脑的 cpu 核心数,
    if (source.length >= maxConcurrency) {
      // 那么就给当前的 promise 再增加一个 then 成功回调,作用是删除在 executing 数组中的自己。
      // 这里需要注意作用域,then 回调里的 e 就是这行代码声明的 e。
      const e = p.then(() => executing.splice(executing.indexOf(e), 1))
      // 往 executing 数组中推入 promise e
      executing.push(e)
      // 在 executing 的长度等于电脑 cpu 核心数后,就使用 Promise.race 执行 execting 中的 Promise。
      // 这样,就能充分利用 cpu 的核心数,达到最快的速度。
      // 原因是 cpu 切换任务执行也是需要耗费时间的。假设一个 cpu 需要执行 3 个任务,
      // 并且这 3 个任务的耗时是确定的,
      // cpu 的第一种方案是等一个任务执行完后再执行下一个任务快;第二种方案是一个任务完成了一半,cpu 又跑了,去执行另一个任务,来来回回执行完三个任务,来回跑动也耗费了点时间。
      // 相比之下,第一种方案更快。
      if (executing.length >= maxConcurrency) {
        await Promise.race(executing)
      }
    }
  }
  return Promise.all(ret)
}

Released under the MIT License.