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 相关文章推荐
验证用户是否修改过页面的数据的实现方法
Sep 26 Javascript
javascript下判断一个对象是否具有指定名称的属性的的代码
Jan 11 Javascript
jQuery学习2 选择器的使用说明
Feb 07 Javascript
setInterval与clearInterval的使用示例代码
Jan 28 Javascript
直接拿来用的页面跳转进度条JS实现
Jan 06 Javascript
angularJs关于指令的一些冷门属性详解
Oct 24 Javascript
JS简单判断函数是否存在的方法
Feb 13 Javascript
BootstrapTable refresh 方法使用实例简单介绍
Feb 20 Javascript
vue-resource 拦截器使用详解
Feb 21 Javascript
React Native 集成jpush-react-native的示例代码
Aug 16 Javascript
一种angular的方法级的缓存注解(装饰器)
Mar 13 Javascript
ng-alain的sf如何自定义部件的流程
Jun 12 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
【星际争霸1】人族1v7家ZBath
2020/03/04 星际争霸
双料怀旧--SHARP GF515的维护、修理和简单调试
2021/03/02 无线电
PHP 5.3新特性命名空间规则解析及高级功能
2010/03/11 PHP
php读取二进制流(C语言结构体struct数据文件)的深入解析
2013/06/13 PHP
详解yii2实现分库分表的方案与思路
2017/02/03 PHP
PHP高效获取远程图片尺寸和大小的实现方法
2017/10/20 PHP
jQuery 使用手册(二)
2009/09/23 Javascript
了解jQuery技巧来提高你的代码(个人觉得那个jquery的手册很不错)
2012/02/10 Javascript
jquery序列化form表单使用ajax提交后处理返回的json数据
2014/03/03 Javascript
如何防止回车(enter)键提交表单
2014/05/11 Javascript
JavaScript字符串对象toUpperCase方法入门实例(用于把字母转换为大写)
2014/10/17 Javascript
JS组件Bootstrap实现弹出框效果代码
2016/04/26 Javascript
jQuery筛选数组之grep、each、inArray、map的用法及遍历json对象
2016/06/20 Javascript
标准的js无缝滚动效果
2016/08/30 Javascript
AngularJs Managing Service Dependencies详解
2016/09/02 Javascript
js 能实现监听F5页面刷新子iframe 而父页面不刷新的方法
2016/11/09 Javascript
Vue+Element实现表格编辑、删除、以及新增行的最优方法
2019/05/28 Javascript
layer.js之回调销毁对话框的例子
2019/09/11 Javascript
vue项目中使用bpmn为节点添加颜色的方法
2020/04/30 Javascript
OpenLayers3实现地图鹰眼以及地图比例尺的添加
2020/09/25 Javascript
Python三级目录展示的实现方法
2016/09/28 Python
python基于物品协同过滤算法实现代码
2018/05/31 Python
python实现输入的数据在地图上生成热力图效果
2019/12/06 Python
英国领先的运动物理治疗供应公司:Vivomed
2018/07/14 全球购物
allbeauty美国:英国在线美容店
2019/03/11 全球购物
Spartoo美国:欧洲排名第一的在线时装零售商
2019/12/12 全球购物
Expedia瑞典官网:预订度假屋、酒店、汽车租赁、机票等
2021/01/23 全球购物
三爱活动实施方案
2014/03/19 职场文书
公司年会策划方案
2014/05/17 职场文书
高中教师先进事迹材料
2014/08/22 职场文书
钳工实训报告总结
2014/11/04 职场文书
2016年社区综治宣传月活动总结
2016/03/16 职场文书
利用前端HTML+CSS+JS开发简单的TODOLIST功能(记事本)
2021/04/13 Javascript
详解Redis基本命令与使用场景
2021/06/01 Redis
nginx中封禁ip和允许内网ip访问的实现示例
2022/03/17 Servers
python的html标准库
2022/04/29 Python