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 相关文章推荐
js常用函数 不错
Sep 08 Javascript
javascript多种数据类型表格排序代码分析
Sep 11 Javascript
JavaScript SetInterval与setTimeout使用方法详解
Nov 15 Javascript
JS连连看源码完美注释版(推荐)
Dec 09 Javascript
js实现Select列表内容自动滚动效果代码
Aug 20 Javascript
jquery实现动画菜单的左右滚动、渐变及图形背景滚动等效果
Aug 25 Javascript
遍历json获得数据的几种方法小结
Jan 21 Javascript
在JS循环中使用async/await的方法
Oct 12 Javascript
vue添加class样式实例讲解
Feb 12 Javascript
泛谈JS逻辑判断选择器 || &amp;&amp;
May 24 Javascript
taro 实现购物车逻辑的实例代码
Jun 05 Javascript
JavaScript实现通讯录功能
Dec 27 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 win下Socket方式发邮件类
2009/08/21 PHP
PHP中使用数组实现堆栈数据结构的代码
2012/02/05 PHP
PHP中extract()函数的定义和用法
2012/08/17 PHP
php中foreach结合curl实现多线程的方法分析
2016/09/22 PHP
CodeMirror2 IE7/IE8 下面未知运行时错误的解决方法
2012/03/29 Javascript
详解JavaScript函数绑定
2013/08/18 Javascript
js/jquery去掉空格,回车,换行示例代码
2013/11/05 Javascript
js判断浏览器类型为ie6时不执行
2014/06/15 Javascript
Javascript对象字面量的理解
2016/06/22 Javascript
鼠标经过出现气泡框的简单实例
2017/03/17 Javascript
PHP 实现一种多文件上传的方法
2017/09/20 Javascript
浅谈使用React.setState需要注意的三点
2017/12/18 Javascript
JavaScript执行环境及作用域链实例分析
2018/08/01 Javascript
实例讲解JavaScript预编译流程
2019/01/24 Javascript
深入浅出了解Node.js Streams
2019/05/27 Javascript
layui禁用侧边导航栏点击事件的解决方法
2019/09/25 Javascript
Vue自定义全局弹窗组件操作
2020/08/11 Javascript
python中去空格函数的用法
2014/08/21 Python
PyQT实现多窗口切换
2018/04/20 Python
Tensorflow中使用tfrecord方式读取数据的方法
2018/06/19 Python
解决python 无法加载downsample模型的问题
2018/10/25 Python
python之列表推导式的用法
2019/11/29 Python
浅析Django 接收所有文件,前端展示文件(包括视频,文件,图片)ajax请求
2020/03/09 Python
Tensorflow tensor 数学运算和逻辑运算方式
2020/06/30 Python
python raise的基本使用
2020/09/10 Python
如何利用CSS3制作3D效果文字具体实现样式
2013/05/02 HTML / CSS
HTML5实现直播间评论滚动效果的代码
2020/05/27 HTML / CSS
Loreto Gallo英国:欧洲领先的在线药房
2021/01/21 全球购物
波兰多品牌运动商店:StreetStyle24.pl
2020/09/22 全球购物
表决心的诗句大全
2014/03/11 职场文书
火灾现场处置方案
2014/05/28 职场文书
竞选纪律委员演讲稿
2014/09/13 职场文书
我们的节日中秋节活动总结
2015/03/23 职场文书
学校重阳节活动总结
2015/03/24 职场文书
2015最新婚礼主持词
2015/06/30 职场文书
python数据库批量插入数据的实现(executemany的使用)
2021/04/30 Python