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 中document.URL 和 windows.location.href 的区别
Nov 11 Javascript
javascript URL编码和解码使用说明
Apr 12 Javascript
jQuery异步加载数据并添加事件示例
Aug 24 Javascript
JavaScript使用yield模拟多线程的方法
Mar 19 Javascript
详解vue2.0脚手架的webpack 配置文件分析
May 27 Javascript
使用JavaScript进行表单校验功能
Aug 01 Javascript
使用vue点击li,获取当前点击li父辈元素的属性值方法
Sep 12 Javascript
js 将线性数据转为树形的示例代码
May 28 Javascript
解决vue组件props传值对象获取不到的问题
Jun 06 Javascript
npm的lock机制解析
Jun 20 Javascript
angular6开发steps步骤条组件
Jul 04 Javascript
JQuery+drag.js上传图片并且实现图片拖曳
Nov 18 jQuery
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 仿Comsenz安装效果代码打包提供下载
2010/05/09 PHP
Windows下Apache + PHP SESSION丢失的解决过程全纪录
2015/04/07 PHP
Yii框架实现邮箱激活的方法【数字签名】
2016/10/18 PHP
JavaScript TO HTML 转换
2006/06/26 Javascript
传智播客学习之java 反射
2009/11/22 Javascript
JavaScript CSS 修改学习第四章 透明度设置
2010/02/19 Javascript
JS 有名函数表达式全面解析
2010/03/19 Javascript
用nodejs写的一个简单项目打包工具
2013/05/11 NodeJs
javascript unicode与GBK2312(中文)编码转换方法
2013/11/14 Javascript
jquery事件重复绑定的快速解决方法
2014/01/03 Javascript
jquery判断元素的子元素是否存在的示例代码
2014/02/04 Javascript
javascript获取浏览器类型和版本的方法(js获取浏览器版本)
2014/03/13 Javascript
js实现简洁的TAB滑动门效果代码
2015/09/06 Javascript
基于BootStrap环境写jQuery tabs插件
2016/07/12 Javascript
利用jQuery解析获取JSON数据
2017/04/08 jQuery
详解AngularJS controller调用factory
2017/05/19 Javascript
vue组件实现可搜索下拉框扩展
2020/10/23 Javascript
node.js连接mysql与基本用法示例
2019/01/05 Javascript
js中console在一行内打印字符串和对象的方法
2019/09/10 Javascript
JavaScript将数组转换为链表的方法
2020/02/16 Javascript
vue 表单输入框不支持focus及blur事件的解决方案
2020/11/17 Vue.js
Python MD5文件生成码
2009/01/12 Python
Python使用poplib模块和smtplib模块收发电子邮件的教程
2016/07/02 Python
Python构建XML树结构的方法示例
2017/06/30 Python
基于python 字符编码的理解
2017/09/02 Python
Python基于time模块求程序运行时间的方法
2017/09/18 Python
python爬虫 正则表达式使用技巧及爬取个人博客的实例讲解
2017/10/20 Python
python实现二分类的卡方分箱示例
2019/11/22 Python
Pycharm 使用 Pipenv 新建的虚拟环境(图文详解)
2020/04/16 Python
Python基础教程(一)——Windows搭建开发Python开发环境
2020/07/20 Python
CSS3实现可爱的小黄人动画
2016/07/11 HTML / CSS
CSS3实现多样的边框效果
2018/05/04 HTML / CSS
吉列剃须刀美国官网:Gillette美国
2018/07/13 全球购物
英国男女豪华配饰和礼品网站:Black.co.uk
2020/02/28 全球购物
使用pipenv管理python虚拟环境的全过程
2021/09/25 Python
Win11远程连接不上怎么办?Win11远程桌面用不了的解决方法
2022/08/05 数码科技