JavaScript异步操作中串行和并行


Posted in Javascript onNovember 20, 2021

1、前言

本文写一下jses5es6针对异步函数,串行执行和并行执行的方案。已经串行和并行结合使用的例子。

2、es5方式

在es6出来之前,社区nodejs中针对回调地狱,已经有了promise方案。假如多个异步函数,执行循序怎么安排,如何才能更快的执行完所有异步函数,再执行下一步呢?这里就出现了js的串行执行和并行执行问题。

3、异步函数串行执行

var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];

function async(arg, callback) {
  console.log('参数为 ' + arg +' , 1秒后返回结果');
  setTimeout(function () { callback(arg * 2); }, 1000);
}

function final(value) {
  console.log('完成: ', value);
}

function series(item) {
  if(item) {
    async( item, function(result) {
      results.push(result);
      return series(items.shift());// 递归执行完所有的数据
    });
  } else {
    return final(results[results.length - 1]);
  }
}

series(items.shift());

4、异步函数并行执行

上面函数是一个一个执行的,上一个执行结束再执行下一个,类似es6(es5之后统称es6)中 async 和await,那有没有类似promise.all这种,所有的并行执行的呢?

可以如下写:

var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];

function async(arg, callback) {
  console.log('参数为 ' + arg +' , 1秒后返回结果');
  setTimeout(function () { callback(arg * 2); }, 1000);
}

function final(value) {
  console.log('完成: ', value);
}

items.forEach(function(item) {// 循环完成
  async(item, function(result){
    results.push(result);
    if(results.length === items.length) {// 判断执行完毕的个数是否等于要执行函数的个数
      final(results[results.length - 1]);
    }
  })
});

5、异步函数串行执行和并行执行结合

假如并行执行很多条异步(几百条)数据,每个异步数据中有很多的(https)请求数据,势必造成tcp 连接数不足,或者堆积了无数调用栈导致内存溢出。所以并行执行不易太多数据,因此,出现了并行和串行结合的方式。

代码可以如下书写:

var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];
var running = 0;
var limit = 2;

function async(arg, callback) {
  console.log('参数为 ' + arg +' , 1秒后返回结果');
  setTimeout(function () { callback(arg * 2); }, 1000);
}

function final(value) {
  console.log('完成: ', value);
}

function launcher() {
  while(running < limit && items.length > 0) {
    var item = items.shift();
    async(item, function(result) {
      results.push(result);
      running--;
      if(items.length > 0) {
        launcher();
      } else if(running == 0) {
        final(results);
      }
    });
    running++;
  }
}

launcher();

6、es6方式

es6天然自带串行和并行的执行方式,例如串行可以用asyncawait(前文已经讲解),并行可以用promise.all等等。那么针对串行和并行结合,限制promise all并发数量,社区也有一些方案,例如

tiny-async-pool、es6-promise-pool、p-limit

简单封装一个promise all并发数限制解决方案函数

function PromiseLimit(funcArray, limit = 5) { // 并发执行5条数据
  let i = 0;
  const result = [];
  const executing = [];
  const queue = function() {
    if (i === funcArray.length) return Promise.all(executing);
    const p = funcArray[i++]();
    result.push(p);
    const e = p.then(() => executing.splice(executing.indexOf(e), 1));
    executing.push(e);
    if (executing.length >= limit) {
      return Promise.race(executing).then(
        () => queue(),
        e => Promise.reject(e)
      );
    }
    return Promise.resolve().then(() => queue());
  };
  return queue().then(() => Promise.all(result));
}

使用:

// 测试代码
const result = [];
for (let index = 0; index < 10; index++) {
  result.push(function() {
    return new Promise((resolve, reject) => {
      console.log("开始" + index, new Date().toLocaleString());
      setTimeout(() => {
        resolve(index);
        console.log("结束" + index, new Date().toLocaleString());
      }, parseInt(Math.random() * 10000));
    });
  });
}

PromiseLimit(result).then(data => {
  console.log(data);
});

修改测试代码,新增随机失败逻辑

// 修改测试代码 随机失败或者成功
const result = [];
for (let index = 0; index < 10; index++) {
  result.push(function() {
    return new Promise((resolve, reject) => {
      console.log("开始" + index, new Date().toLocaleString());
      setTimeout(() => {
        if (Math.random() > 0.5) {
          resolve(index);
        } else {
          reject(index);
        }
        console.log("结束" + index, new Date().toLocaleString());
      }, parseInt(Math.random() * 1000));
    });
  });
}
PromiseLimit(result).then(
  data => {
    console.log("成功", data);
  },
  data => {
    console.log("失败", data);
  }
);

7、async 和await 结合promise all

async function PromiseAll(promises,batchSize=10) {
 const result = [];
 while(promises.length > 0) {
   const data = await Promise.all(promises.splice(0,batchSize));
   result.push(...data);
 }
return result;
}

这么写有2个问题:

  • 1、在调用Promise.all前就已经创建好了promises,实际上promise已经执行了
  • 2、你这个实现必须等前面batchSize个promise resolve,才能跑下一批的batchSize个,也就是promise all全部成功才可以。

改进如下:

async function asyncPool(array,poolLimit,iteratorFn) {
  const ret = [];
  const executing = [];
  for (const item of array) {
    const p = Promise.resolve().then(() => iteratorFn(item, array));
    ret.push(p);

    if (poolLimit <= array.length) {
      const e = p.then(() => executing.splice(executing.indexOf(e), 1));
      executing.push(e);
      if (executing.length >= poolLimit) {
        await Promise.race(executing);
      }
    }
  }
  return Promise.all(ret);
}

使用:

const timeout = i => new Promise(resolve => setTimeout(() => resolve(i), i));
return asyncPool( [1000, 5000, 3000, 2000], 2,timeout).then(results => {
    ...
});

到此这篇关于JavaScript异步操作中串行和并行的文章就介绍到这了,更多相关JavaScript异步操作串行和并行内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
jQuery 技巧大全(新手入门篇)
May 12 Javascript
不一样的文字闪烁 轮番闪烁
Nov 11 Javascript
加载列表时jquery获取ul中第一个li的属性
Nov 02 Javascript
javascript的日期对象、数组对象、二维数组使用说明
Dec 22 Javascript
js实现网页右上角滑出会自动消失大幅广告的方法
Feb 27 Javascript
javascript将DOM节点添加到文档的方法实例分析
Aug 04 Javascript
json对象转为字符串,当做参数传递时加密解密的实现方法
Jun 29 Javascript
vue 实现剪裁图片并上传服务器功能
Mar 01 Javascript
解决在vue项目中,发版之后,背景图片报错,路径不对的问题
Mar 06 Javascript
vue路由事件beforeRouteLeave及组件内定时器的清除方法
Sep 29 Javascript
jQuery实现为table表格动态添加或删除tr功能示例
Feb 19 jQuery
vue实现倒计时获取验证码效果
Apr 17 Javascript
vue中 this.$set的使用详解
如何用vue实现网页截图你知道吗
利用 JavaScript 构建命令行应用
Nov 17 #Javascript
Ajax实现异步加载数据
Nov 17 #Javascript
36个正则表达式(开发效率提高80%)
Nov 17 #Javascript
Javascript 解构赋值详情
Nov 17 #Javascript
javascript Number 与 Math对象的介绍
Nov 17 #Javascript
You might like
php录入页面中动态从数据库中提取数据的实现
2006/10/09 PHP
用穿越火线快速入门php面向对象
2012/02/22 PHP
深入解析PHP的Yii框架中的event事件机制
2016/03/17 PHP
php使用preg_match()函数验证ip地址的方法
2017/01/07 PHP
PHP切割汉字的常用方法实例总结
2019/04/27 PHP
PHP面向对象程序设计重载(overloading)操作详解
2019/06/13 PHP
Safari5中alert的无限循环BUG
2011/04/07 Javascript
利用毫秒减值计算时长的js代码
2013/09/22 Javascript
js中的push和join方法使用介绍
2013/10/08 Javascript
js获取UserControl内容为拼html时提供方便
2014/11/02 Javascript
jQuery实现仿Alipay支付宝首页全屏焦点图切换特效
2015/05/04 Javascript
基于ajax实现文件上传并显示进度条
2015/08/03 Javascript
JS组件Bootstrap按钮组与下拉按钮详解
2016/05/10 Javascript
jQuery+CSS3实现四种应用广泛的导航条制作实例详解
2016/09/17 Javascript
js基础之DOM中元素对象的属性方法详解
2016/10/28 Javascript
jQuery实现radio第一次点击选中第二次点击取消功能
2017/05/15 jQuery
Vue resource中的GET与POST请求的实例代码
2017/07/21 Javascript
JS对象与JSON互转换、New Function()、 forEach()、DOM事件流等js开发基础小结
2017/08/10 Javascript
Vue 兄弟组件通信的方法(不使用Vuex)
2017/10/26 Javascript
浅谈React中的元素、组件、实例和节点
2018/02/27 Javascript
vue 监听屏幕高度的实例
2018/09/05 Javascript
Vue 实现输入框新增搜索历史记录功能
2019/10/15 Javascript
python映射列表实例分析
2015/01/26 Python
Python中关键字nonlocal和global的声明与解析
2017/03/12 Python
Python学习入门之区块链详解
2017/07/25 Python
不到20行代码用Python做一个智能聊天机器人
2019/04/19 Python
Pandas之Dropna滤除缺失数据的实现方法
2019/06/25 Python
tensorflow基于CNN实战mnist手写识别(小白必看)
2020/07/20 Python
巴基斯坦电子产品购物网站:Home Shopping
2017/09/14 全球购物
Notino瑞典:购买香水和美容产品
2019/07/26 全球购物
印刷工程专业应届生求职信
2013/09/29 职场文书
婚礼主持词
2014/03/13 职场文书
教室布置标语
2014/06/26 职场文书
个人批评与自我批评总结
2014/10/17 职场文书
世界卫生日宣传活动总结
2015/02/09 职场文书
远程教育学习心得体会
2016/01/23 职场文书