深入理解Promise.all


Posted in Javascript onAugust 08, 2018

异步之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,和timeout
    console.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) {
 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。我们就要找出这个原因,那就是找到了为什么要使用箭头函数。

首先我通过调试来查找

如图:

深入理解Promise.all

程序首先打印出了

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

说明一定是先执行了

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

所以我们在这段打上断点进行一步一步调试,如下:

深入理解Promise.all

根据上图我们可以看出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方法将会被执行,输出结果为:2
  console.log(2);
});

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript自适应宽度的瀑布流实现思路
Feb 20 Javascript
js 高效去除数组重复元素示例代码
Dec 19 Javascript
jquery实现公告翻滚效果
Feb 27 Javascript
JavaScript中逗号运算符介绍及使用示例
Mar 13 Javascript
JavaScript检测弹出窗口是否已经关闭的方法
Mar 24 Javascript
简介JavaScript中Math.LOG10E属性的使用
Jun 14 Javascript
跟我学习javascript的for循环和for...in循环
Nov 18 Javascript
你有必要知道的25个JavaScript面试题
Dec 29 Javascript
JS两种类型的表单提交方法实例分析
Nov 28 Javascript
Vue2实时监听表单变化的示例讲解
Aug 30 Javascript
VUE中使用MUI方法
Feb 12 Javascript
环形加载进度条封装(Vue插件版和原生js版)
Dec 04 Javascript
vue js秒转天数小时分钟秒的实例代码
Aug 08 #Javascript
vue devtools的安装与使用教程
Aug 08 #Javascript
jQuery AJAX 方法success()后台传来的4种数据详解
Aug 08 #jQuery
通过jquery的ajax请求本地的json文件方法
Aug 08 #jQuery
Vue 开发音乐播放器之歌手页右侧快速入口功能
Aug 08 #Javascript
jQuery中ajax请求后台返回json数据并渲染HTML的方法
Aug 08 #jQuery
Vue2.0 实现歌手列表滚动及右侧快速入口功能
Aug 08 #Javascript
You might like
php 404错误页面实现代码
2009/06/22 PHP
php数组中包含中文的排序方法
2014/06/03 PHP
phpmyadmin提示The mbstring extension is missing的解决方法
2014/12/17 PHP
thinkPHP的表达式查询用法详解
2016/09/14 PHP
PHP _construct()函数讲解
2019/02/03 PHP
用javascript实现计算两个日期的间隔天数
2007/08/14 Javascript
JavaScript入门学习书籍推荐
2008/06/12 Javascript
JQuery加载图片自适应固定大小的DIV
2013/09/12 Javascript
jquery parent和parents的区别分析
2013/10/02 Javascript
onmouseover事件和onmouseout事件全面理解
2016/08/15 Javascript
JavaScript算法系列之快速排序(Quicksort)算法实例详解
2016/09/04 Javascript
jQuery查找和过滤_动力节点节点Java学院整理
2017/07/04 jQuery
JS和jQuery通过this获取html标签中的属性值(实例代码)
2017/09/11 jQuery
详解node Async/Await 更好的异步编程解决方案
2018/05/10 Javascript
javascript数据结构之多叉树经典操作示例【创建、添加、遍历、移除等】
2018/08/01 Javascript
微信小程序按钮点击跳转页面详解
2019/05/06 Javascript
python的tkinter布局之简单的聊天窗口实现方法
2014/09/03 Python
python多进程和多线程究竟谁更快(详解)
2017/05/29 Python
Python3实现的简单验证码识别功能示例
2018/05/02 Python
python中报错"json.decoder.JSONDecodeError: Expecting value:"的解决
2019/04/29 Python
Python 循环终止语句的三种方法小结
2019/06/24 Python
django搭建项目配置环境和创建表过程详解
2019/07/22 Python
Python Dict找出value大于某值或key大于某值的所有项方式
2020/06/05 Python
Keras自定义实现带masking的meanpooling层方式
2020/06/16 Python
python实现学生信息管理系统(精简版)
2020/11/27 Python
5分钟让你掌握css3阴影、倒影、渐变小技巧(小编推荐)
2016/08/15 HTML / CSS
捷克玩具商店:Bambule
2019/02/23 全球购物
俄罗斯大型在线书店:Читай-город
2019/10/10 全球购物
施华洛世奇巴西官网:SWAROVSKI巴西
2019/12/03 全球购物
科颜氏印度官网:Kiehl’s印度
2021/02/20 全球购物
写一个方法1000的阶乘
2012/11/21 面试题
医务人员医德考评自我评价
2015/03/03 职场文书
MySQL CHAR和VARCHAR该如何选择
2021/05/31 MySQL
深入浅析python3 依赖倒置原则(示例代码)
2021/07/09 Python
实现一个简单得数据响应系统
2021/11/11 Javascript
AudioContext 实现音频可视化(web技术分享)
2022/02/24 Javascript