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

一、并行

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

我们最经常用的,是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. kbuild系统中最简单的目标 -- help(三)

    当你学完help目标后你会发现它与我们的代码基本没有什么直接的关系,仅仅是用来生成kbuild的简短使用说明,但是用它来作为学习掌握kbuild编译系统的开篇,不管是从难度还是学习感受上真的是再适合不 ...

  2. 解决Oracle jdbc驱动包maven下载失败问题

    由于Oracle版权限制,其jdbc驱动包不让人随便下载,这就给maven的下载和编译带来了麻烦. 解决办法是先获取jar包(方法一:去oracle官网下载,方法二:去oracle安装目录如produ ...

  3. 将Dubbo注册到Nacos,与DubboAdmin的部署

    王有志,一个分享硬核Java技术的互金摸鱼侠加入Java人的提桶跑路群:共同富裕的Java人 本文是<从 0 开始学 Dubbo>系列文章中应用篇的番外篇. 在这篇文章中我会和大家一起部署 ...

  4. test20230304考试总结(2023春 · 字符串)

    前言 赛时得分明细: A B C D Total Rank 100 100 0 70 270 2 C题如此说道:字符串没有学好的报应!! A. P4391 [BOI2009]Radio Transmi ...

  5. 行行AI人才直播第14期:【国内第二波人工智能进入者、连续创业者】土豆《土豆利用GPT成功融资两次的提示词和故事》

    行行AI人才(海南行行智能科技有限公司)是博客园和顺顺智慧共同运营的AI行业人才全生命周期服务平台. 此刻,ChatGPT的火热程度已经无需多言.一时间,追逐大模型成了国内AI行业的标准动作,&quo ...

  6. Python 潮流周刊第 13 期(2023-07-29)

    查看全文: https://pythoncat.top/posts/2023-07-29-weekly 文章&教程 1.Jupyter Notebook 7 隆重发布 (英) 2.Python ...

  7. Vu3+Element-Plus根据路由配置生成菜单导航栏

    先看效果,整体界面结构如下 点击左侧菜单栏,右侧切换显示不同页面内容. Vue3使用路由–南河小站 1 路由配置 路由配置如下: const routes = [ { path: "&quo ...

  8. [kubernetes]二进制部署k8s集群-基于containerd

    0. 前言 k8s从1.24版本开始不再直接支持docker,但可以自行调整相关配置,实现1.24版本后的k8s还能调用docker.其实docker自身也是调用containerd,与其k8s通过d ...

  9. Airtest的iOS实用接口介绍

    1. 前言 前段时间Airtest更新了1.3.0.1版本,里面涉及非常多的iOS功能新增和改动,今天想详细跟大家聊一下里面的iOS设备接口. PS:本文示例均使用本地连接的iOS设备,Airtest ...

  10. 一文读懂LockSupport

    阅读本文前,需要储备的知识点如下,点击链接直接跳转. java线程详解 Java不能操作内存?Unsafe了解一下 LockSupport介绍 搞java开发的基本都知道J.U.C并发包(即java. ...