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 相关文章推荐
用JAVASCRIPT如何给&amp;lt;textarea&amp;gt;&amp;lt;/textarea&amp;gt;赋值
Apr 20 Javascript
jquery 必填项判断表单是否为空的方法
Sep 14 Javascript
Jquery 监视按键,按下回车键触发某方法的实现代码
May 11 Javascript
在JavaScript中操作时间之setYear()方法的使用
Jun 12 Javascript
javascript封装 Cookie 应用接口
Aug 07 Javascript
谈谈JavaScript中的几种借用方法
Aug 09 Javascript
js html css实现复选框全选与反选
Oct 09 Javascript
jQuery中 $ 符号的冲突问题及解决方案
Nov 04 Javascript
Bootstrap实现导航栏的2种方式
Nov 28 Javascript
浅谈vue-router 路由传参的方法
Dec 27 Javascript
微信小程序实现拼图小游戏
Oct 22 Javascript
JS中循环遍历数组的四种方式总结
Jan 23 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
PhpMyAdmin出现export.php Missing parameter: what /export_type错误解决方法
2012/08/09 PHP
PHP使用Session遇到的一个Permission denied Notice解决办法
2014/07/30 PHP
PHP中使用array函数新建一个数组
2015/11/19 PHP
WordPress中用于获取文章作者与分类信息的方法整理
2015/12/17 PHP
yii2高级应用之自定义组件实现全局使用图片上传功能的方法
2016/10/08 PHP
jQuery 图像裁剪插件Jcrop的简单使用
2009/05/22 Javascript
javascript 表单验证常见正则
2009/09/28 Javascript
javascript 循环调用示例介绍
2013/11/20 Javascript
js实现防止被iframe的方法
2015/07/03 Javascript
JavaScript数据绑定实现一个简单的 MVVM 库
2016/04/08 Javascript
Vue开发过程中遇到的疑惑知识点总结
2017/01/20 Javascript
JavaScript Uploadify文件上传实例
2017/02/28 Javascript
Vue工程模板文件 webpack打包配置方法
2017/12/26 Javascript
JS遍历JSON数组及获取JSON数组长度操作示例【测试可用】
2018/12/12 Javascript
深入理解 JS 垃圾回收
2019/06/03 Javascript
Node.js使用MongoDB的ObjectId作为查询条件的方法
2019/09/10 Javascript
基于JavaScript实现表格隔行换色
2020/05/08 Javascript
Python实现的tab文件操作类分享
2014/11/20 Python
Python通过poll实现异步IO的方法
2015/06/04 Python
浅谈Python类里的__init__方法函数,Python类的构造函数
2016/12/10 Python
python实现BackPropagation算法
2017/12/14 Python
ubuntu16.04制作vim和python3的开发环境
2018/09/23 Python
python实现多张图片拼接成大图
2019/01/15 Python
python中实现控制小数点位数的方法
2019/01/24 Python
python调用webservice接口的实现
2019/07/12 Python
Python post请求实现代码实例
2020/02/28 Python
米兰网婚纱礼服法国网上商店:Milanoo法国
2016/08/20 全球购物
10条PHP编程习惯
2014/05/26 面试题
化学学院毕业生自荐信范文
2013/12/17 职场文书
前台文员我鉴定
2014/01/12 职场文书
荷叶母亲教学反思
2014/04/30 职场文书
收银员岗位职责
2015/02/03 职场文书
2019年鼓励无偿献血倡议书
2019/09/17 职场文书
go语言中json数据的读取和写出操作
2021/04/28 Golang
《勇者辞职不干了》ED主题曲无字幕动画MV公开
2022/04/13 日漫
Python实现Matplotlib,Seaborn动态数据图
2022/05/06 Python