Promise.all的深入理解

异步之Promise

Promise.all

Promise.all接收的promise数组是按顺序执行的还是一起执行的,也就是说返回的结果是顺序固定的吗?

目前有两种答案:

  1. 应该是同步执行的,但是这样就有效率问题了,如果想改成异步执行怎么办呢?
  2. 有些人认为结果是按顺序执行的,有些人认为结果顺序不确定。

那么我们根据实现来解密:

环境为:

vscode 1.20.1
node   v8.9.0
npm    v5.6.0

实验代码:

// 获取随机数,toFixed为四舍五入保留小数,0为保留整数,范围~1000
const getRandom = () => +(Math.random()*1000).toFixed(0);const asyncTask = (taskID) => new Promise( (resolve) => {// 随机获取一次0~1000的随机数let timeout = getRandom();// 打印出传递进来的ID号 taskID=1 start.console.log(`taskID=${taskID} start.`);// 设置计时时间,function()等价于 () => {...}setTimeout(function() {// 打印出执行的taskID,和timeoutconsole.log(`taskID=${taskID} finished in time=${timeout}.`);// 异步成功执行resolve(taskID)}, timeout);
});Promise.all([asyncTask(1),asyncTask(2),asyncTask(3)])
.then(resultList => {console.log('results:',resultList);
});

实验结果如下:

第一次

taskID=1 start.
taskID=2 start.
taskID=3 start.
taskID=2 finished in time=321.
taskID=3 finished in time=506.
taskID=1 finished in time=932.
results:
Array(3) [1, 2, 3]

第二次

taskID=1 start.
taskID=2 start.
taskID=3 start.
taskID=1 finished in time=243.
taskID=3 finished in time=305.
taskID=2 finished in time=792.
results:
Array(3) [1, 2, 3]

第三次

taskID=1 start.
taskID=2 start.
taskID=3 start.
taskID=3 finished in time=380.
taskID=1 finished in time=539.
taskID=2 finished in time=782.
results:
Array(3) [1, 2, 3]

补充知识介绍

// toFixed() 方法可把 Number 四舍五入为指定小数位数的数字。
NumberObject.toFixed(num)
// num	必需。规定小数的位数,是 0 ~ 20 之间的值,包括 0 和  
// 20,有些实现可以支持更大的数值范围。如果省略了该参数,将用 0 代替。

Promise构造函数只有一个参数,该参数是一个函数,被称作执行器,执行器有2个参数,分别是resolve()和reject(),一个表示成功的回调,一个表示失败的回调。

new Promise(function(resolve, reject) {setTimeout(() => resolve(5), 0)
}).then(v => console.log(v)) // 5

记住,Promise实例只能通过resolve或者reject函数来返回,并且使用then()或者catch()获取,不能在new Promise里面直接return,这样是获取不到Promise返回值的。


由此可见,Promise.all 里的任务列表[asyncTask(1),asyncTask(2),asyncTask(3)],我们是按照顺序发起的。
但是根据结果来说,它们是异步的,互相之间并不阻塞,每个任务完成时机是不确定的,尽管如此,所有任务结束之
后,它们的结果仍然是按顺序地映射到resultList里,这样就能和Promise.all里的任务列表
[asyncTask(1),asyncTask(2),asyncTask(3)]一一对应起来。

深入理解Promise.all()

*可能看到这里有些人没有清楚,为什么返回一个数组? *
我们在来看一下这段代码:

Promise.all([asyncTask(1),asyncTask(2),asyncTask(3)])
.then(resultList => {console.log('results:',resultList);
});

通常我们在使用异步的时候都是只有一个Promise,现在我们使用all()方法包装多个Promise实例。

语法很简单:参数只有一个,可迭代对象,可以是数组,或者Symbol类型等。

Promise.all(iterable).then().catch()

传入3个Promise实例:

Promise.all([new Promise(function(resolve, reject) {resolve(1)}),new Promise(function(resolve, reject) {resolve(2)}),new Promise(function(resolve, reject) {resolve(3)})
]).then(arr => {console.log(arr) // [1, 2, 3]
})

那么我们回头想想应该明白了吧?
因为我们传入的是数组,那么返回的必须是数组,并且会将讲过进行映射。

Promise.race()

语法和all()一样,但是返回值有所不同,race根据传入的多个Promise实例,只要有一个实例resolve或者reject,就只返回该结果,其他实例不再执行。

我们简单看一下例子,返回结果为3,因为我们设置了定时器,第三个Promise执行的最快。

Promise.race([new Promise(function(resolve, reject) {setTimeout(() => resolve(1), 1000)}),new Promise(function(resolve, reject) {setTimeout(() => resolve(2), 100)}),new Promise(function(resolve, reject) {setTimeout(() => resolve(3), 10)})
]).then(value => {console.log(value) // 3
})

异步为什么使用箭头函数

这是我一直困惑的原因,我们将前面的例子进行改造一下。

如下:

const getRandom = () => +(Math.random()*1000).toFixed(0);function test(taskID) {return new Promise( (resolve) => {// 随机获取一次0~1000的随机数let timeout = getRandom();// 打印出传递进来的ID号console.log(`taskID=${taskID} start.`);setTimeout(function() {console.log(`taskID=${taskID} finished in time=${timeout}.`);resolve(taskID)}, timeout);
} )
}Promise.all([test(1),test(2),test(3)])
.then(resultList => {console.log('results:',resultList);
});

我们先来看一下结果是怎样的?

第一次:

taskID=1 start.
taskID=2 start.
taskID=3 start.
results:
Array(3) [undefined, undefined, undefined]
taskID=1 finished in time=460.
taskID=2 finished in time=704.
taskID=3 finished in time=883.

第二次:

taskID=1 start.
taskID=2 start.
taskID=3 start.
results:
Array(3) [undefined, undefined, undefined]
taskID=2 finished in time=17.
taskID=3 finished in time=212.
taskID=1 finished in time=612.

第三次:

taskID=1 start.
taskID=2 start.
taskID=3 start.
results:
Array(3) [undefined, undefined, undefined]
taskID=3 finished in time=130.
taskID=1 finished in time=256.
taskID=2 finished in time=593.

实验还是要至少做上3次以上才有说服力。

通过输出结果我们能够看出返回的数组内的数据都为undefined。我们就要找出这个原因,那就是找到了为什么要使用箭头函数。

  1. 首先我通过调试来查找
    如图:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oZTdOow8-1628238283115)(https://note.youdao.com/yws/api/personal/file/WEBcfbe51c67215e9414c1ac940d99e0caa?method=download&shareKey=c3945e0b4a991911953e4986d51b11cd)]

程序首先打印出了

taskID=1 start.
taskID=2 start.
taskID=3 start.

说明一定是先执行了

console.log(`taskID=${taskID} start.`);

所以我们在这段打上断点进行一步一步调试,如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vVqeaAeA-1628238283117)(https://note.youdao.com/yws/api/personal/file/WEBdae375d2f798007f6390ff75f746943b?method=download&shareKey=3c091febde5a6b7e582e7a261b7b8854)]

根据上图我们可以看出console.log(taskID=${taskID} start.)每次都会被执行,setTimeout也会被执行,但是3次之后,就会直接开始执行.then(),所以我们找到了原因,Promise.all()这时并没有等待返回完整的数据就执行了.then(),没有等到resolve就开始执行了。

说明这里面出现了异常,而这个异常就是由于Promise.all()内的参数,存在函数,造成this混淆,所以我们要使用对象,更准确的说法就是***实例***。

注意:

以这段代码为例:

var p1 = Promise.resolve(1),p2 = Promise.resolve(2),p3 = Promise.resolve(3);
Promise.all([p1, p2, p3]).then(function (results) {console.log(results);  // [1, 2, 3]
});

在上面的方法中,promise数组中所有的promise实例都变为resolve的时候,该方法才会返回,并将所有结果传递results数组中。promise数组中任何一个promise为reject的话,则整个Promise.all调用会立即终止,并返回一个reject的新的promise对象。reject使用示例如下:

var p1 = Promise.resolve(1),p2 = Promise.reject(2),p3 = Promise.resolve(3);
Promise.all([p1, p2, p3]).then(function (results) {//then方法不会被执行console.log(results); 
}).catch(function (e){//catch方法将会被执行,输出结果为:2console.log(2);
});

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/536139.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

wordpress后台无法登录问题

之前给自己的WordPress加了个标签云,今天登录的时候突然发现网站后台进不去了,无奈各种找材料,这算是皇天不负有心人,总算是给我找到了,现在做一下记录 登录不上的原因在于:wp-admin和wp-admin/是不同的&a…

深入理解Angular订阅者模式

深入理解Angular订阅者模式 如果正在读此篇文章的你学过java,c++等面向对象语言,知道两个模式观察者模式和订阅者模式,分别为:Observer pattern,Pub-sub pattern(Subscriber) 接下来我们结合Angular来说明这两个模式。 Observer pattern This is a pattern of developme…

Ubuntu中安装python3

通过命令行安装Python3.*,只需要在终端中通过命令行安装即可: sudo apt-get install python3 Ubuntu的底层大多数采用的是Python2.*,Python3和Python2是互相不兼容的,完全没法通用的(也不知道他们怎么想的o(TヘTo)&a…

Angular深入理解之指令

Angular深入理解之指令 指令有什么功能 Attribute directives 属性指令Structural directives 结构指令自定义属性指令自定义结构指令Angular深入理解之指令 对于初学Angular的同学来说,指令无疑是最痛苦的,那么我们怎么使用自定义的指令呢?指令到底怎么实现呢?为什么要写…

windows下Apache虚拟主机配置

找到host文件:C:\Windows\System32\drivers\etc\hosts 在hosts这么增加: 127.0.0.1 666.666.com 127.0.0.1 777.777.com 修改httpd.conf文件: 打开文件:xxx\xampp\apache\conf\httpd.conf 找到#LoadModule vhost_…

Angular深入理解基本组成

Angular深入理解基本组成 在讲指令时,我们先来了解一下Angular的基本概念和结构。 Module 模块 Angular 是模块化的.Modules 导出 classes, function, values , 以便在其他模块导入使用.angular应用由模块组成,每个模块都做此模块相关的事情组件、方法、类、服务等,他们都…

1607: 字符棱形

1607: 字符棱形 根据读入的字符和边长,勾画字符棱形。 Input 输入数据含有不超过50组的数据,每组数据包括一个可见字符c和一个整数n(1≤n≤30)。 Output 输出以c为填充字符,边长为n的棱形,勾画每个棱形…

Angular深入理解管道Pipe

Angular深入理解管道 纯管道与非纯管道区别的本质 Pure FunctionImpure Function内置Pipe pipe使用自定义Pipe 管道性能优化Angular深入理解管道 管道的链接 有学过linux shell的同学,应该知道管道,在shell中的管道是IPC,linux的进程间通讯有pipe,FIFO,signal。这里只是简单…

1959: 图案打印

1959: 图案打印 Description 一年一度的植树节就要到了,计算机学院学生准备在学院教学楼门前的空地上种植树木。为使树木排列得更加美观,大家决定把树木排列成菱形。现在告诉你我们所拥有的树木能排列成边长为N的菱形,请你编程输出树木所排…

JS事件的捕获和冒泡阶段

JS事件的捕获和冒泡阶段 这里介绍两个事件模型&#xff1a;IE事件模型与DOM事件模型 IE内核浏览器的事件模型是冒泡型事件&#xff08;没有捕获事件过程&#xff09;&#xff0c;事件句柄的触发顺序是从ChildNode到ParentNode。 <div id"ancestor"> <butt…

2016: C语言实验——打印金字塔

2016: C语言实验——打印金字塔 Description 输入n值&#xff0c;打印下列形状的金字塔&#xff0c;其中n代表金字塔的层数。 Input 输入只有一个正整数n。 Output 打印金字塔图形&#xff0c;其中每个数字之间有一个空格。 Sample Input 3 Sample Output 11 2 1 1 2 …

Anuglar中正确导入RxJS库

Anuglar中正确导入RxJS库 目前Angular2中的已经内建支持RxJS,所以我们在使用的时候可以直接导入使用了。 理解操作符导⼊ 在使用创建依赖于 RxJS 组件,服务,指令等等时, 你可能遇到处理运算符导⼊的问 题。 在项⽬中引⼊操作符最主要的⽅式像下⾯这样导⼊: import rxj…

1495: 蛇行矩阵

1495: 蛇行矩阵 Description 蛇形矩阵是由1开始的自然数依次排列成的一个矩阵上三角形。 Input 本题有多组数据&#xff0c;每组数据由一个正整数N组成。&#xff08;N不大于100&#xff09; Output 对于每一组数据&#xff0c;输出一个N行的蛇形矩阵。两组输出之间不要额…

递归基础之N皇后问题

递归基础之N皇后问题 Description 在nn 格的棋盘上放置彼此不受攻击的n 个皇后。按照国际象棋的规则&#xff0c;皇后可以攻击与之 处在同一行或同一列或同一斜线上的棋子。n后问题等价于在nn格的棋盘上放置n个皇后&#xff0c; 任何2 个皇后不放在同一行或同一列或同一斜线上…

X86和X86_64和AMD64的由来

为什么叫X86和X86_64和AMD64 为什么大家叫x86为32位系统呢 相信大家在大学里面有很多人都玩过8086&#xff08;微处理器&#xff09;&#xff0c;这是一个可编程的系统&#xff0c;他是由intel开发的&#xff0c;英特尔出了划时代的8086之后&#xff0c;后来使用该架构出了80…

回炉-熄灯问题

进来突然意识到算法的重要性&#xff0c;可惜已经没有充足的时间去进行专业的训练了&#xff0c;只能慢慢挤时间做几个题练习一下聊以安慰&#xff0c;希望能多坚持几天吧&#xff0c;奉劝各位想学算法的同学一定要趁早啊。 poj1222 解析见郭炜老师的程序设计与算法&#xff…

ngrx初识

ngrx初识 在使用之前需要安装ngrx npm install @ngrx/store --save 或者 yarn add @ngrx/store ngrx/store:保存了ReduxAPI的核心概念,使用RxJS扩展的Redux实现。使用可观察对象来简化了监听事件的订阅等操作。 dispatch&reducer&state dispatcher,reducer,state…

回炉-特殊密码锁

题目&#xff1a;特殊密码锁 001:特殊密码锁 描述 有一种特殊的二进制密码锁&#xff0c;由n个相连的按钮组成&#xff08;n<30&#xff09;&#xff0c;按钮有凹/凸两种状态&#xff0c;用手按按钮会改变其状态。 然而让人头疼的是&#xff0c;当你按一个按钮时&#x…

Angular的NgModule

Angular的NgModule NgModule作为Angular模块的核心,也是组织者,官方有很长的文档来介绍他,包括每一个API。 @NgModule文件的定义方式 import { BrowserModule } from @angular/platform-browser; import { NgModule } from @angular/core;import { AppComponent } from ./ap…

回炉-拨钟问题

题目&#xff1a;拨钟问题 1166:拨钟问题 描述 有9个时钟&#xff0c;排成一个3*3的矩阵。 |-------| |-------| |-------| | | | | | | | |---O | |---O | | O | | | | | | | |-------| |-------| …