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实现的激活输入框后隐藏初始内容
Jun 29 Javascript
jquery 简短右键菜单 多浏览器兼容
Jan 01 Javascript
Javascript实现的鼠标经过时播放声音
May 18 Javascript
使用jQuery实现验证上传图片的格式与大小
Dec 03 Javascript
JQuery 在文档中查找指定name的元素并移除的实现方法
May 19 Javascript
jQuery实现圣诞节礼物传送(花式轮播)
Dec 25 Javascript
微信小程序 图片绝对定位(背景图片)
Apr 05 Javascript
详解创建自定义的Angular Schematics
Jun 06 Javascript
layui前端框架之table表数据的刷新方法
Aug 17 Javascript
对angularJs中$sce服务安全显示html文本的实例
Sep 30 Javascript
微信小程序发布新版本时自动提示用户更新的方法
Jun 07 Javascript
Vue中this.$nextTick的作用及用法
Feb 04 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/06/25 PHP
PHP+redis实现的悲观锁机制示例
2018/06/12 PHP
javascript 隔行换色函数代码
2010/10/24 Javascript
javascript中的return和闭包函数浅析
2014/06/06 Javascript
node.js中的fs.rename方法使用说明
2014/12/16 Javascript
浅谈jQuery事件绑定原理
2015/01/02 Javascript
Jquery 全选反选实例代码
2015/11/19 Javascript
详解AngularJS Filter(过滤器)用法
2015/12/28 Javascript
初步使用bootstrap快速创建页面
2016/03/03 Javascript
jQuery Mobile页面返回不需要重新get
2016/04/26 Javascript
JavaScript cookie详解及简单实例应用
2016/12/31 Javascript
详解js的异步编程技术的方法
2017/02/09 Javascript
微信小程序 ES6Promise.all批量上传文件实现代码
2017/04/14 Javascript
详解基于angular-cli配置代理解决跨域请求问题
2017/07/05 Javascript
vue生成随机验证码的示例代码
2017/09/29 Javascript
webpack 4.0.0-beta.0版本新特性介绍
2018/02/10 Javascript
node.js express框架简介与实现
2019/07/23 Javascript
Python绘制KS曲线的实现方法
2018/08/13 Python
python Tkinter的图片刷新实例
2019/06/14 Python
详解Python对JSON中的特殊类型进行Encoder
2019/07/15 Python
关于pytorch中网络loss传播和参数更新的理解
2019/08/20 Python
django 多对多表的创建和插入代码实现
2019/09/09 Python
python 操作hive pyhs2方式
2019/12/21 Python
python基于selenium爬取斗鱼弹幕
2021/02/20 Python
Java基础面试题
2014/07/19 面试题
中专生自我鉴定
2013/12/17 职场文书
委托证明的格式
2014/01/10 职场文书
检察官就职演讲稿
2014/01/13 职场文书
小学校长先进事迹材料
2014/05/13 职场文书
法律专业求职信
2014/05/24 职场文书
竞选大学学委演讲稿
2014/09/13 职场文书
老干部工作汇报材料
2014/10/28 职场文书
领导欢迎词范文
2015/01/26 职场文书
军训结束新闻稿
2015/07/17 职场文书
宾馆卫生管理制度
2015/08/06 职场文书
Nginx部署vue项目和配置代理的问题解析
2021/08/04 Servers