JS异步任务的并行、串行,以及二者结合
让多个异步任务按照我们的想法执行,是开发中常见的需求。今天我们就来捋一下,如何让多个异步任务并行,串行,以及并行串行相结合。
一、并行
并行是使用最多的方式,多个相互间没有依赖关系的异步任务,并行执行能够提高效率。
我们最经常用的,是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异步任务的并行、串行,以及二者结合的更多相关文章
- Python并发编程系列之常用概念剖析:并行 串行 并发 同步 异步 阻塞 非阻塞 进程 线程 协程
1 引言 并发.并行.串行.同步.异步.阻塞.非阻塞.进程.线程.协程是并发编程中的常见概念,相似却也有却不尽相同,令人头痛,这一篇博文中我们来区分一下这些概念. 2 并发与并行 在解释并发与并行之前 ...
- 搞定 CompletableFuture,并发异步编程和编写串行程序还有什么区别?你们要的多图长文
你有一个思想,我有一个思想,我们交换后,一个人就有两个思想 If you can NOT explain it simply, you do NOT understand it well enough ...
- GCD,用同步/异步函数,创建并发/串行队列
队列 第一个参数:C语言字符串,标签 第二个参数: DISPATCH_QUEUE_CONCURRENT:并发队列 DISPATCH_QUEUE_SERIAL:串行队列 dispatch_queue_ ...
- GCD之并行串行区别
1.用户自定义线程队列,创建时很容易创建 注意创建时的第一个参数:标记值,方便调试查看 1 2 dispatch_queue_t serialqueue=dispatch_queue_create(& ...
- iOS:转载:同步、异步、并行、串行的详解
理解 iOS 开发中 GCD 相关的同步(synchronization)\ 异步(asynchronization),串行(serial)\ 并行(concurrency)概念 2014年11月21 ...
- ios多线程操作(五)—— GCD串行队列与并发队列
GCD的队列能够分为2大类型,分别为串行队列和并发队列 串行队列(Serial Dispatch Queue): 一次仅仅调度一个任务,队列中的任务一个接着一个地运行( ...
- IOS GCD(线程的 串行、并发 基本使用)
什么是GCD 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器” 纯C语言,提供了非常多强大的函数 GCD的优势 GCD是苹果公司为多核的并行运算提出的解决方案 GCD会自 ...
- 【原】iOS多线程之异步任务+并行队列情况与异步任务+串行队列(主队列)情况
异步任务+并行队列 把异步任务放到并行队列进行执行,异步任务会在不同的线程中执行. /*异步执行+并行队列*/ - (IBAction)clickBasic1:(UIButton *)sender { ...
- iOS:GCD理解1(同步-异步、串行-并行)
1.并行-异步(ST1与ST2抢占资源) 1-1).获取 并行(全局)队列 ,DISPATCH_QUEUE_PRIORITY_DEFAULT 为默认优先级. dispatch_queue_t queu ...
- ios--进程/多线程/同步任务/异步任务/串行队列/并行队列(对比分析)
现在先说两个基本的概念,啥是进程,啥是线程,啥又是多线程;先把这两个总是给弄清再讲下面的 进程:正在进行的程序,我们就叫它进程. 线程:线程就是进程中的一个独立的执行路径.这句话怎么理解呢! 一个程序 ...
随机推荐
- 如何开发 RESTful、GraphQL 和 SOAP 等不同类型的 API ?
在软件开发中,API(应用程序编程接口)的重要性不言而喻.API已成为不可或缺的构建模块,使开发人员能够创建功能丰富.多样化和可扩展的应用程序.这是一篇综合指南,旨在深入探讨API开发,使初学者和有经 ...
- virt-install 使用 qcow2格式虚拟机镜 、macvtap网卡
安装虚拟机 这里使用 amazn2 虚拟机镜像安装,根据官网文档,需要预先配置一个 seed.iso 文件 参考文档:https://docs.aws.amazon.com/zh_cn/AWSEC2/ ...
- Android Studio开发小项目
"莆仙小馆"--莆田文化展示APP 文化展示程序目的在于应用科学技术助推家乡优秀传统文化的展示与交流.通过图片.视频.音频等展示方式向用户立体地展示一个文化城邦.传统文化与科学技术 ...
- Django: request.GET.get()
释义 query = request.GET.get('name', '') 寻找名为name的GET参数,而且如果参数没有提交,返回一个空的字符串. 对比request.GET() 如果使用requ ...
- Cesium-加载3D飞机模型沿指定路线前进
https://blog.csdn.net/Apple_Coco/article/details/108882146
- 牛客小白月赛65 E题 题解
原题链接 题意描述 构造一个\(1\)到\(n\)的排列,使得其中正好有\(k\)个二元组\((i, j)\)满足,\(1\le i\lt j\le n\) && \(a_i - a_ ...
- WPF实现类似ChatGPT的逐字打印效果
背景 前一段时间ChatGPT类的应用十分火爆,这类应用在回答用户的问题时逐字打印输出,像极了真人打字回复消息.出于对这个效果的兴趣,决定用WPF模拟这个效果. 真实的ChatGPT逐字输出效果涉及其 ...
- 4.2 C++ Boost 内存池管理库
Boost 库是一个由C/C++语言的开发者创建并更新维护的开源类库,其提供了许多功能强大的程序库和工具,用于开发高质量.可移植.高效的C应用程序.Boost库可以作为标准C库的后备,通常被称为准标准 ...
- 从头到尾说一次 Spring 事务管理(器)
事务管理,一个被说烂的也被看烂的话题,还是八股文中的基础股之一. 本文会从设计角度,一步步的剖析 Spring 事务管理的设计思路(都会设计事务管理器了,还能玩不转?) 为什么需要事务管理? 先看看 ...
- 微服务下使用maven做多环境配置
分享技术,用心生活 前言:很多项目在开发,提测,上线时都会提前手动改一些配置文件来适应对应环境,麻烦不说了,而且也容易出错:生产环境的配置也容易暴露.基于此,我们基于spring cloud alib ...