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 相关文章推荐
Cookie 小记
Apr 01 Javascript
jQuery的学习步骤
Feb 23 Javascript
autoPlay 基于jquery的图片自动播放效果
Dec 07 Javascript
Javascript基础教程之argument 详解
Jan 18 Javascript
AngularJS中的模块详解
Jan 29 Javascript
JS基于Mootools实现的个性菜单效果代码
Oct 21 Javascript
通过bootstrap全面学习less
Nov 09 Javascript
关于Vue.js一些问题和思考学习笔记(2)
Dec 02 Javascript
Vue使用vue-area-linkage实现地址三级联动效果的示例
Jun 27 Javascript
koa-router源码学习小结
Sep 07 Javascript
如何让node运行es6模块文件及其原理详解
Dec 11 Javascript
解决vuex改变了state的值,但是页面没有更新的问题
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自定义的格式化时间示例代码
2013/12/05 PHP
zf框架的session会话周期及次数限制使用示例
2014/03/13 PHP
PHP+jQuery+Ajax实现分页效果 jPaginate插件的应用
2015/10/09 PHP
学习php设计模式 php实现观察者模式(Observer)
2015/12/09 PHP
php闭包中使用use声明变量的作用域实例分析
2018/08/09 PHP
解决laravel(5.5)访问public报错的问题
2019/10/12 PHP
jquery之empty()与remove()区别说明
2010/09/10 Javascript
禁用页面部分JavaScript不是全部而是部分
2014/09/03 Javascript
jQuery+jsp下拉框联动获取本地数据的方法(附源码)
2015/12/03 Javascript
hovertree插件实现二级树形菜单(简单实用)
2016/12/28 Javascript
JavaScript仿支付宝6位数字密码输入框
2016/12/29 Javascript
JS简单获取日期相差天数的方法
2017/04/24 Javascript
express框架实现基于Websocket建立的简易聊天室
2017/08/10 Javascript
jquery插件开发之选项卡制作详解
2017/08/30 jQuery
webpack多入口多出口的实现方法
2018/08/17 Javascript
js中自定义react数据验证组件实例详解
2018/10/19 Javascript
详解在vue-cli3.0中自定css、js和图片的打包路径
2019/08/26 Javascript
js中apply和call的理解与使用方法
2019/11/27 Javascript
vue-路由精讲 二级路由和三级路由的作用
2020/08/06 Javascript
python小技巧之批量抓取美女图片
2014/06/06 Python
对于Python编程中一些重用与缩减的建议
2015/04/14 Python
python+matplotlib绘制3D条形图实例代码
2018/01/17 Python
Python中类的创建和实例化操作示例
2019/02/27 Python
python3.6下Numpy库下载与安装图文教程
2019/04/02 Python
Python 模拟生成动态产生验证码图片的方法
2020/02/01 Python
美国东北部户外服装和设备零售商:Eastern Mountain Sports
2016/10/05 全球购物
高中生学习生活的自我评价
2013/10/09 职场文书
新娘父亲婚礼致辞
2014/01/16 职场文书
翻译学院毕业生自荐书
2014/02/02 职场文书
带薪年假请假条
2014/02/04 职场文书
授权委托书
2014/09/17 职场文书
2014法院四风问题对照检查材料思想汇报
2014/10/04 职场文书
2014年实习期工作总结
2014/11/27 职场文书
初中作文评语
2014/12/25 职场文书
小学体育课教学反思
2016/02/16 职场文书
Windows 11上手初体验:任务栏和开始菜单等迎来大改
2021/11/21 数码科技