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 可以拖动的div实现代码 脚本之家修正版
Jun 26 Javascript
学习ExtJS Column布局
Oct 08 Javascript
在Ubuntu上安装最新版本的Node.js
Jul 14 Javascript
jQuery使用height()获取高度需要注意的地方
Dec 13 Javascript
分享javascript、jquery实用代码段
Oct 20 Javascript
jQuery取得元素标签名称小结(附代码)
Aug 16 jQuery
微信小程序实现手势图案锁屏功能
Jan 30 Javascript
vue中使用protobuf的过程记录
Oct 26 Javascript
jQuery实现表格的增、删、改操作示例
Jan 27 jQuery
JavaScript数组去重的方法总结【12种方法,号称史上最全】
Feb 28 Javascript
ES6中异步对象Promise用法详解
Jul 31 Javascript
RxJS在TypeScript中的简单使用详解
Apr 13 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 图像尺寸调整代码
2010/05/26 PHP
浅析PHP编程中10个最常见的错误
2014/08/08 PHP
PHP中的魔术方法总结和使用实例
2015/05/11 PHP
XHProf报告字段含义的解析
2016/05/17 PHP
JSON.parse()和JSON.stringify()使用介绍
2014/06/20 Javascript
JS中完美兼容各大浏览器的scrolltop方法
2015/04/17 Javascript
Javascript json object 与string 相互转换的简单实现
2016/09/27 Javascript
Angularjs实现带查找筛选功能的select下拉框示例代码
2016/10/04 Javascript
yarn与npm的命令行小结
2016/10/20 Javascript
AngularJS实现用户登录状态判断的方法(Model添加拦截过滤器,路由增加限制)
2016/12/12 Javascript
详解JS对象封装的常用方式
2016/12/30 Javascript
js实现移动端编辑添加地址【模仿京东】
2017/04/28 Javascript
详解从angular-cli:1.0.0-beta.28.3升级到@angular/cli:1.0.0
2017/05/22 Javascript
详解使用Visual Studio Code对Node.js进行断点调试
2017/09/14 Javascript
微信小程序实现动态设置placeholder提示文字及按钮选中/取消状态的方法
2017/12/14 Javascript
node+vue实现文件上传功能
2020/05/28 Javascript
Python使用Socket(Https)Post登录百度的实现代码
2012/05/18 Python
举例详解Python中yield生成器的用法
2015/08/05 Python
浅析Python中的for 循环
2016/06/09 Python
python 第三方库的安装及pip的使用详解
2017/05/11 Python
pycharm远程调试openstack代码
2017/11/21 Python
pandas object格式转float64格式的方法
2018/04/10 Python
利用matplotlib为图片上添加触发事件进行交互
2020/04/23 Python
django 解决model中类写不到数据库中,数据库无此字段的问题
2020/05/20 Python
python/golang 删除链表中的元素
2020/09/14 Python
美国一家运动专业鞋类零售商:Warehouse Shoe Sale(WSS)
2018/03/28 全球购物
汉森批发:Hansen Wholesale
2018/05/24 全球购物
美国在线纱线商店:Darn Good Yarn
2019/03/20 全球购物
个人实用的自我评价范文
2013/11/23 职场文书
给老师的一封建议书
2014/03/13 职场文书
公共场所禁烟倡议书
2014/08/30 职场文书
新手开公司创业注意事项有哪些?
2019/07/29 职场文书
anaconda python3.8安装后降级
2021/06/11 Python
Python多个MP4合成视频的实现方法
2021/07/16 Python
SpringBoot快速入门详解
2021/07/21 Java/Android
win10如何快速切换窗口 win10切换窗口快捷键分享
2022/07/23 数码科技