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实现给图片加链接
Aug 15 Javascript
默认让页面的第一个控件选中的javascript代码
Dec 26 Javascript
Jquery CheckBox全选方法代码附js checkbox全选反选代码
Jun 09 Javascript
js下关于onmouseout、事件冒泡的问题经验小结
Dec 09 Javascript
根据邮箱的域名跳转到相应的登录页面的代码
Feb 27 Javascript
js/jquery获取浏览器窗口可视区域高度和宽度以及滚动条高度实现代码
Dec 17 Javascript
调用DOM对象的focus使文本框获得焦点
Feb 19 Javascript
jquery动态调整div大小使其宽度始终为浏览器宽度
Jun 06 Javascript
基于Bootstrap实现图片轮播效果
May 22 Javascript
Vue实现动态显示textarea剩余字数
May 22 Javascript
vueScroll实现移动端下拉刷新、上拉加载
Mar 22 Javascript
Vue3项目打包后部署到服务器 请求不到后台接口解决方法
Feb 06 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 中的str_replace 函数总结
2007/04/27 PHP
php strrpos()与strripos()函数
2013/08/31 PHP
php-fpm配置详解
2014/02/12 PHP
适用于初学者的简易PHP文件上传类
2015/10/29 PHP
php opendir()列出目录下所有文件的实例代码
2016/10/02 PHP
jquery实现商品拖动选择效果代码(自写)
2013/05/28 Javascript
jquery提取元素里的纯文本不包含span等里的内容
2013/09/30 Javascript
自己编写的类似JS的trim方法
2013/10/09 Javascript
js截取中英文字符串、标点符号无乱码示例解读
2014/04/17 Javascript
jQuery调用Webservice传递json数组的方法
2016/08/06 Javascript
js设置文字颜色的方法示例
2016/12/30 Javascript
微信小程序 input输入框详解及简单实例
2017/01/10 Javascript
深入理解vue-router之keep-alive
2017/08/31 Javascript
jQuery滚动条美化插件nicescroll简单用法示例
2018/04/18 jQuery
vue项目引入Iconfont图标库的教程图解
2018/10/24 Javascript
微信小程序新闻网站详情页实例代码
2020/01/10 Javascript
jQuery实现可以扩展的日历
2020/12/01 jQuery
[35:55]完美世界DOTA2联赛PWL S3 Rebirth vs CPG 第一场 12.11
2020/12/13 DOTA
构建Python包的五个简单准则简介
2015/06/15 Python
Python编程求解二叉树中和为某一值的路径代码示例
2018/01/04 Python
Python自定义一个类实现字典dict功能的方法
2019/01/19 Python
python实现XML解析的方法解析
2019/11/16 Python
关于python pycharm中输出的内容不全的解决办法
2020/01/10 Python
opencv 实现特定颜色线条提取与定位操作
2020/06/02 Python
CSS3 3D制作实战案例分析
2016/09/18 HTML / CSS
法国珠宝店:CLEOR
2017/01/29 全球购物
匡威比利时官网:Converse Belgium
2017/04/13 全球购物
意大利奢侈品购物网站:Deliberti
2019/10/08 全球购物
丑小鸭教学反思
2014/02/03 职场文书
幼儿评语大全
2014/04/30 职场文书
歌颂祖国的演讲稿
2014/05/04 职场文书
销售2014年度工作总结
2014/12/08 职场文书
小班教师个人总结
2015/02/05 职场文书
Filebeat 采集 Nginx 日志的方法
2021/03/31 Servers
HAM-2000摩机图
2021/04/22 无线电
Flink 侧流输出源码示例解析
2022/09/23 Servers