几百个数据,Promise.all没做控制并发?你心可真大!

发表于:2023-1-13 09:40

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:林三心不学挖掘机    来源:前端大全

  需求
  我最近在做一个需求:批量去往数据库里存储一些东西,数量可能一次性达到几百个,也就意味着我需要一次性往数据库里存储几百次,我是这么做的:
  const save = (data) => {
    // 数据库操作(Promise)
    return insert(data)
  }
  const datas = [...几百个数据]
  // 进行存储
  Promise.all(datas.map(save))
  被呵斥
  正当我觉得这个需求很轻松的时候。。在 Code Review 上,我被团队的大佬们给呵斥了一顿,理由是:存储的操作发生在服务器,服务器是很脆弱的,你一次性存储几百个,服务器崩了怎么办?
  随后大佬们提出解决方案:控制并发,大佬们是真的强,感觉这种东西已经是大佬们的常规操作了。
  控制Promise.all并发
  意思就是,比如我有几百个存储操作,我不能一次性去全部执行,而是要控制一次性只能执行10个操作,10个中有一个执行完了,就拿还没执行的操作补上去,就这样一直到这几百个操作全部执行完为止。。
  其实很简单,可以直接用库,比如async-pool、es6-promise-pool、p-limit,只要是能用库的,我建议不要自己去写,因为不定因素很多,你自己写的肯定没有库写的好,你说呢~
  简单实现
  看到一位兄弟实现的挺不错的,链接:https://segmentfault.com/a/1190000016389127
  这是async-pool这个库的核心源码:
  function asyncPool(poolLimit, array, iteratorFn) {
      let i = 0;
      const ret = [];
      const executing = [];
      const enqueue = function () {
          // 边界处理,array为空数组
          if (i === array.length) {
              return Promise.resolve();
          }
          // 每调一次enqueue,初始化一个promise
          const item = array[i++];
          const p = Promise.resolve().then(() => iteratorFn(item, array));
          // 放入promises数组
          ret.push(p);
          // promise执行完毕,从executing数组中删除
          const e = p.then(() => executing.splice(executing.indexOf(e), 1));
          // 插入executing数字,表示正在执行的promise
          executing.push(e);
          // 使用Promise.rece,每当executing数组中promise数量低于poolLimit,就实例化新的promise并执行
          let r = Promise.resolve();
          if (executing.length >= poolLimit) {
              r = Promise.race(executing);
          }
          // 递归,直到遍历完array
          return r.then(() => enqueue());
      };
      return enqueue().then(() => Promise.all(ret));
  }
  大概的逻辑可以总结为:
  ·从array第1个元素开始,初始化promise对象,同时用一个executing数组保存正在执行的promise
  · 不断初始化promise,直到达到poolLimt
  · 使用Promise.race,获得executing中promise的执行情况,当有一个promise执行完毕,继续初始化promise并放入executing中
  · 所有promise都执行完了,调用Promise.all返回
  使用方式:
  const timeout = i => new Promise(resolve => setTimeout(() => resolve(i), i));
  return asyncPool(2, [1000, 5000, 3000, 2000], timeout).then(results => {
      ...
  });
  本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号