深入理解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 相关文章推荐
客户端脚本中常常出现的一些问题和调试技巧
Jan 09 Javascript
JS、CSS加载中的小问题探讨
Nov 26 Javascript
JavaScript修改作用域外变量的方法
Mar 25 Javascript
详解Bootstrap插件
Apr 25 Javascript
Bootstarp 基础教程之表单部分实例代码
Feb 03 Javascript
angular.fromJson与toJson方法用法示例
May 17 Javascript
Vue + Vue-router 同名路由切换数据不更新的方法
Nov 20 Javascript
postman自定义函数实现 时间函数的思路详解
Apr 17 Javascript
解决Vue 给mapState中定义的属性赋值报错的问题
Jun 22 Javascript
VUE 实现element upload上传图片到阿里云
Aug 12 Javascript
ant design 日期格式化的实现
Oct 27 Javascript
Vue实现购物车基本功能
Nov 08 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
改德生G88 - 加装等响度低音提升电路
2021/03/02 无线电
如何在PHP中使用Oracle数据库(4)
2006/10/09 PHP
PHP中Date()时间日期函数的使用方法小结
2011/04/20 PHP
四个常见html网页乱码问题及解决办法
2015/09/08 PHP
PHP mkdir创建文件夹实现方法解析
2020/11/13 PHP
JavaScript DOM学习第一章 W3C DOM简介
2010/02/19 Javascript
js 函数调用模式小结
2011/12/26 Javascript
jquery导航制件jquery鼠标经过变色效果示例
2013/12/05 Javascript
分享20款美化网站的 jQuery Lightbox 灯箱插件
2014/10/10 Javascript
jQuery.Uploadify插件实现带进度条的批量上传功能
2016/06/08 Javascript
基于JavaScript实现自动更新倒计时效果
2016/12/19 Javascript
JQuery ZTree使用方法详解
2017/01/07 Javascript
jQuery插件HighCharts实现的2D条状图效果示例【附demo源码下载】
2017/03/15 Javascript
bootstrap table动态加载数据示例代码
2017/03/25 Javascript
js实现按座位号抽奖
2017/04/05 Javascript
vue.js实现条件渲染的实例代码
2017/06/22 Javascript
AngularJs 禁止模板缓存的方法
2017/11/28 Javascript
js实现for循环跳过undefined值示例
2019/07/02 Javascript
小程序开发之模态框组件封装
2020/04/23 Javascript
通过数据库对Django进行删除字段和删除模型的操作
2015/07/21 Python
pandas读取csv文件,分隔符参数sep的实例
2018/12/12 Python
python批量从es取数据的方法(文档数超过10000)
2018/12/27 Python
解决Django中修改js css文件但浏览器无法及时与之改变的问题
2019/08/31 Python
完美解决Django2.0中models下的ForeignKey()问题
2020/05/19 Python
学习Python爬虫的几点建议
2020/08/05 Python
Python解析微信dat文件的方法
2020/11/30 Python
使用CSS3实现SVG路径描边动画效果入门教程
2019/10/21 HTML / CSS
html5 video标签屏蔽右键视频另存为的js代码
2013/11/12 HTML / CSS
Gina Bacconi官网:吉娜贝康尼连衣裙和礼服
2018/04/24 全球购物
教师应聘自荐信范文
2014/03/14 职场文书
小学生植树节活动总结
2014/07/04 职场文书
2014年民政工作总结
2014/11/26 职场文书
离婚案件答辩状
2015/05/22 职场文书
浅谈Python实现opencv之图片色素的数值运算和逻辑运算
2021/06/23 Python
Win11安装升级时提示“该电脑必须支持安全启动”
2022/04/19 数码科技
Win10多屏显示如何设置?Win10电脑多屏显示设置操作方法
2022/07/07 数码科技