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

一、并行

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

我们最经常用的,是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. VSCode设置第三方字体

    最近需要写C,所以下了一个VSCode IDE嘛 当然是美观最重要了(不是 个人比较喜欢JetBrains家的字体 在IDEA中主要使用的是 JetBrains Mono 想着把VSCode的字体也设 ...

  2. Maven项目配置

    pom.xml配置 配置编码格式为UTF-8 <properties> <project.build.sourceEncoding>UTF-8</project.buil ...

  3. 【EF Core】主从实体关系与常见实体关系的区别

    上次老周扯了有关主.从实体的话题,本篇咱们再挖一下,主.从实体之间建立的关系,跟咱们常用的一对一.一对多这些关系之间有什么不同. 先看看咱们从学习数据库开始就特熟悉的常用关系--多对多.一对一.一对多 ...

  4. windows CMD命令的一些使用方法及注意事项

    windows CMD命令的一些使用方法及注意事项 转载请著名出处:https://www.cnblogs.com/funnyzpc/p/17572397.html 一.执行路径或参数带中文.空格.特 ...

  5. 【入门教程】3202年了,还有人没用stable diffusion画过自己的AI小姐姐吗。

    个人绘画作品: 说明 本文主要是讲一下如何安装.使用整合包,以及介绍画真人图片的大模型(介绍的整合包只提供二次元模型,个人不太感兴趣) 通过最简单的介绍帮助大家快速入门,开始画图,不会深入的进行讲解, ...

  6. 2023牛客暑期多校训练营5 ABCDEGHI

    比赛链接 A 题解 知识点:莫队,树状数组. 区间询问显然可以离线莫队,考虑端点移动对答案的影响. 不妨先考虑右端点右移一个位置,对答案的改变.假设右端点右移后在 \(r\) ,我们先要知道 \([l ...

  7. [kvm]cpu内存硬盘配置

    修改CPU配置 如果配置了最大CPU # 临时 virsh setvcpus test 2 # 永久 virsh setvcpus test 2 --config 热增加虚拟机的CPU数后,使用lsc ...

  8. vue3 + ElementPlus 封装函数式弹窗组件

    需求场景:弹窗组件需要支持自定义的插槽内容,删除的弹窗也要使用这个组件,只是样式不一样而已,希望在父组件使用删除弹窗的时候直接调用某个方法就可以显示弹窗 组件模拟 cuDialog 假设我的弹窗组件有 ...

  9. Ubuntu安装后续工作

    更新源: sudo gedit /etc/apt/sources.list 清华的源 deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial ma ...

  10. 结果过滤器—MVC项目中结果过滤器(Result Filter)使用

    一.什么是结果过滤器? 结果过滤器(ResultFilter),是对执行的Action结果进行处理的一种AOP思想,适用于任何需要直接环绕 View 或格式化处理的逻辑.结果过滤器可以替换或更改 Ac ...