让多个异步任务按照我们的想法执行,是开发中常见的需求。今天我们就来捋一下,如何让多个异步任务并行,串行,以及并行串行相结合。

一、并行

并行是使用最多的方式,多个相互间没有依赖关系的异步任务,并行执行能够提高效率。

我们最经常用的,是Promise.all() 。

function f1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('1结束');
resolve();
}, 1000)
});
}
function f2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('2结束');
resolve();
}, 900)
});
}
function f3() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('3结束');
resolve();
}, 800)
});
} let arr = [f1, f2, f3];
Promise.all(arr.map(i => i()));
// 3结束
// 2结束
// 1结束

以下几种数组遍历方式,同样可以实现并行。

// forEach遍历
arr.forEach(item => {
item();
});
// for循环
for (let i = 0; i < arr.length; i++) {
arr[i]();
}
// for...of遍历
for (let item of arr) {
item();
}

// 注意,以下两种写法同样是并行的
arr.forEach(async item => await item());
async function f() {
arr.forEach(async item => await item())
}
f();

相比之下,Promise.all()可以确保任务都执行成功,然后再执行后续操作,这是各种遍历无法做到的。

另外,还有一种方式也能实现并行:Promise.allSettled()。

Promise.allSettled(arr.map(i => i()));

这种方式很特别,它无法得到每个Promise对象的返回值,却可以精确得知每个任务的成功还是失败。如果你有这样的需求场景,用Promise.allSettled()就很合适。

二、串行

我在工作中遇到过一个场景,一个有1000+元素的数组,每个成员都是调用第三方接口的Promise对象。我像往常一样得意的使用Promise.all(),等着1000多个任务瞬间完成。然而,结果却让我大跌眼镜,这1000多个任务,只有一部分成功了,大部分都报错了。不管我执行几次,结果都是这个样。一筹莫展之后,我才从第三方那儿得知,他们的接口是有调用限制的,一个接口同一时间只能并行300个。

有没有办法能让它们一个接一个的执行呢?也就是串行。

nodejs koa框架的next()语法给了我启发,它就是让中间件一个接一个的执行。于是我想出了递归的方式:

async function serial(arr) {
let item = arr.shift(); await item();
if (arr.length > 0) {
await serial(arr);
}
}
serial(arr);
// 1结束
// 2结束
// 3结束

只要把一个Promise对象数组传给它,数组成员就会依次顺序执行。

其实,想让异步任务串行,不用这么麻烦。以下遍历的方式,同样可以实现串行。

// 使用for...of
async function f() {
for (let item of arr) {
await item();
}
}
f(); // 使用for循环
async function f() {
for (let i = 0; i < arr.length; i++) {
await arr[i]();
}
}
f();

发现了没?为什么同样是for循环,同样是for...of,前面的写法是并行,后面就成了串行呢?

工作中,我们一定做过这样的尝试,想通过遍历,来让多个异步任务串行。但往往不得其法,怎么折腾它们都还是同时执行。

后一种写法,你可以理解为:await执行完成后,才会进入下一次循环。 其实,遍历,就相当于把每一个元素,在代码中从上到下写下来。当它们处于async函数中,并在每个元素前面加await,它们自然就能顺序执行。否则,我们都知道,简单的顺序写下来的异步任务,它们还是同时执行的。

好了,现在程序不报错了。但是,1000多个任务依次执行完成,足足花了十多分钟,太慢了!有没有办法,又快又不触发接口调用限制呢?

有,如果可以并行200个任务,完成后再开始下一轮200个......也就是,把并行和串行相结合。

三、并行串行结合

async function bingChuan(arr, num) {
let items = arr.splice(0, num); await Promise.all(items.map(i => i()));
if (arr.length > 0) {
await bingChuan(arr, num);
}
}
bingChuan(arr, 2);
// 2结束
// 1结束
// 3结束

好了,现在可以同时享有并行和串行的好处了!

本人水平非常有限,写作主要是为了把自己学过的东西捋清楚。如有错误,还请指正,感激不尽。

JS异步任务的并行、串行,以及二者结合的更多相关文章

  1. Python并发编程系列之常用概念剖析:并行 串行 并发 同步 异步 阻塞 非阻塞 进程 线程 协程

    1 引言 并发.并行.串行.同步.异步.阻塞.非阻塞.进程.线程.协程是并发编程中的常见概念,相似却也有却不尽相同,令人头痛,这一篇博文中我们来区分一下这些概念. 2 并发与并行 在解释并发与并行之前 ...

  2. 搞定 CompletableFuture,并发异步编程和编写串行程序还有什么区别?你们要的多图长文

    你有一个思想,我有一个思想,我们交换后,一个人就有两个思想 If you can NOT explain it simply, you do NOT understand it well enough ...

  3. GCD,用同步/异步函数,创建并发/串行队列

    队列  第一个参数:C语言字符串,标签 第二个参数: DISPATCH_QUEUE_CONCURRENT:并发队列 DISPATCH_QUEUE_SERIAL:串行队列 dispatch_queue_ ...

  4. GCD之并行串行区别

    1.用户自定义线程队列,创建时很容易创建 注意创建时的第一个参数:标记值,方便调试查看 1 2 dispatch_queue_t serialqueue=dispatch_queue_create(& ...

  5. iOS:转载:同步、异步、并行、串行的详解

    理解 iOS 开发中 GCD 相关的同步(synchronization)\ 异步(asynchronization),串行(serial)\ 并行(concurrency)概念 2014年11月21 ...

  6. ios多线程操作(五)—— GCD串行队列与并发队列

          GCD的队列能够分为2大类型,分别为串行队列和并发队列      串行队列(Serial Dispatch Queue):      一次仅仅调度一个任务,队列中的任务一个接着一个地运行( ...

  7. IOS GCD(线程的 串行、并发 基本使用)

    什么是GCD 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器” 纯C语言,提供了非常多强大的函数 GCD的优势 GCD是苹果公司为多核的并行运算提出的解决方案 GCD会自 ...

  8. 【原】iOS多线程之异步任务+并行队列情况与异步任务+串行队列(主队列)情况

    异步任务+并行队列 把异步任务放到并行队列进行执行,异步任务会在不同的线程中执行. /*异步执行+并行队列*/ - (IBAction)clickBasic1:(UIButton *)sender { ...

  9. iOS:GCD理解1(同步-异步、串行-并行)

    1.并行-异步(ST1与ST2抢占资源) 1-1).获取 并行(全局)队列 ,DISPATCH_QUEUE_PRIORITY_DEFAULT 为默认优先级. dispatch_queue_t queu ...

  10. ios--进程/多线程/同步任务/异步任务/串行队列/并行队列(对比分析)

    现在先说两个基本的概念,啥是进程,啥是线程,啥又是多线程;先把这两个总是给弄清再讲下面的 进程:正在进行的程序,我们就叫它进程. 线程:线程就是进程中的一个独立的执行路径.这句话怎么理解呢! 一个程序 ...

随机推荐

  1. zynq7000 emc启动及其加速

    背景需求 ZYNQ 7000系统在出场时需要将固件从eMMC启动,原因有2: FLASH存储空间小: SD卡容易脱落,不适合产品存放系统文件: 需要注意,ZYNQ7000 系列不支持eMMC作为BOO ...

  2. Windows商店开发者注册失败

    前言 最近写了个小工具想上架Windows应用商店,但是在填写信息那一页总是失败,提示Error code 2201. Correlation ID 9d436e3a-94df-498a-b224-8 ...

  3. Blazor前后端框架Known-V1.2.6

    V1.2.6 Known是基于C#和Blazor开发的前后端分离快速开发框架,开箱即用,跨平台,一处代码,多处运行. Gitee: https://gitee.com/known/Known Gith ...

  4. 王道oj/problem12(动态申请内存存储数组)

    网址:http://oj.lgwenda.com/problem/12 思路:用输入的整型创建对应数组,用scanf消除换行键: 用gets()输入语句并输出,再释放. 代码: #define _CR ...

  5. 快速入门OpenCv(python版)

    OpenCV是一个(开源)发行的跨平台计算机视觉库,可以运行在Linux.Windows和Mac OS操作系统上.它轻量级而且高效--由一系列 C 函数和少量 C++ 类构成,同时提供了Python. ...

  6. 你知道ES6中的这些属性吗

    ES6,也称ESMAScript2015,这个版本增加了很多好用的特性 变量声明 ES6之前用var来定义变量,ES6增加了两个变量声明的方式,分别为const和let,const用来定义常量,let ...

  7. auto-GPT部署

    Auto-GPT 是一个实验性开源应用程序,其作者在3月31日将其发布在Github上.它以GPT-4 作为驱动,可以自主做出决定以实现目标,无需用户干预.AutoGPT的地址:https://git ...

  8. BUUCTF-Youngter-drive 双线程的思考

    拿到后先upx脱壳,然后直接进 从上向下看 应该是输入 慢慢看 明显的加密 这个函数大概是个加密 但是这个sleep函数是什么鬼??? 以下将拓展些许有关"线程"的概念:    一 ...

  9. 为什么NoSQL不支持事务

    为什么NoSQL不支持事务 1. 背景 看书<Neo4j权威指南>的时候,发现个问题:日常的NoSQL都不支持事务(ACID). 2. 问题 事务对数据的存储过程是有利的,既然事情是有利的 ...

  10. Java 多线程处理 for 循环数据

    对于必须在for循环内进行查询的场景,可采用以下几种方式进行优化. 1.主线程与子线程无先后顺序 public static void main(String[] args) throws Inter ...