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 相关文章推荐
firefox中用javascript实现鼠标位置的定位
Jun 17 Javascript
jQuery操作select的实例代码
Jun 14 Javascript
等待指定时间后自动跳转或关闭当前页面的js代码
Jul 09 Javascript
JS滚轮事件onmousewheel使用介绍
Nov 01 Javascript
Javascript遍历table中的元素示例代码
Jul 08 Javascript
jquery预加载图片的方法
May 27 Javascript
在JavaScript中使用开平方根的sqrt()方法
Jun 15 Javascript
JavaScript函数柯里化详解
Apr 29 Javascript
Javascript基础回顾之(一) 类型
Jan 31 Javascript
vue中使用vue-router切换页面时滚动条自动滚动到顶部的方法
Nov 28 Javascript
JavaScript实现连连看连线算法
Jan 05 Javascript
react-native聊天室|RN版聊天App仿微信实例|RN仿微信界面
Nov 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
php中的观察者模式
2010/03/24 PHP
php中Smarty模板初体验
2011/08/08 PHP
Zend Framework教程之Zend_Config_Xml用法分析
2016/03/23 PHP
Laravel登录失败次数限制的实现方法
2020/08/26 PHP
jQuery的一些注意
2006/12/06 Javascript
js中将字符串转换成json的三种方式
2011/01/12 Javascript
关于eval 与new Function 到底该选哪个?
2013/04/17 Javascript
jQuery中的$.ajax()方法应用
2014/05/06 Javascript
Node.js实现简单聊天服务器
2014/06/20 Javascript
深入浅析NodeJs并发异步的回调处理
2015/12/21 NodeJs
浅谈addEventListener和attachEvent的区别
2016/07/14 Javascript
浅谈JS函数定义方式的区别
2016/10/30 Javascript
JS匿名函数类生成方式实例分析
2016/11/26 Javascript
详解用node搭建简单的静态资源管理器
2017/08/09 Javascript
手把手教你vue-cli单页到多页应用的方法
2018/05/31 Javascript
vue vue-Router默认hash模式修改为history需要做的修改详解
2018/09/13 Javascript
cocos2dx+lua实现橡皮擦功能
2018/12/20 Javascript
JavaScript实现更换背景图片
2019/10/18 Javascript
vue Treeselect下拉树只能选择第N级元素实现代码
2020/08/31 Javascript
利用python操作SQLite数据库及文件操作详解
2017/09/22 Python
pycharm设置注释颜色的方法
2018/05/23 Python
python 发送和接收ActiveMQ消息的实例
2019/01/30 Python
关于阿里云oss获取sts凭证 app直传 python的实例
2019/08/20 Python
pytorch实现建立自己的数据集(以mnist为例)
2020/01/18 Python
Python定时从Mysql提取数据存入Redis的实现
2020/05/03 Python
python中return如何写
2020/06/18 Python
Python + opencv对拍照得到的图片进行背景去除的实现方法
2020/11/18 Python
美国睫毛、眉毛精华液领导品牌:RevitaLash Cosmetics
2018/03/26 全球购物
桥梁与隧道工程专业本科生求职信
2013/10/08 职场文书
DIY蛋糕店的创业计划书范文
2013/12/26 职场文书
中文专业毕业生自荐信
2014/05/24 职场文书
土建施工员岗位职责
2014/07/16 职场文书
商场圣诞节活动总结
2015/05/06 职场文书
数据库的高级查询六:表连接查询:外连接(左外连接,右外连接,UNION关键字,连接中ON与WHERE的不同)
2021/04/05 MySQL
mysql的数据压缩性能对比详情
2021/11/07 MySQL
Go语言grpc和protobuf
2022/04/13 Golang