异步之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);
});

Promise.all的深入理解相关推荐

  1. 大白话讲解Promise(二)理解Promise规范

    上一篇我们讲解了ES6中Promise的用法,但是知道了用法还远远不够,作为一名专业的前端工程师,还必须通晓原理.所以,为了补全我们关于Promise的知识树,有必要理解Promise/A+规范,理解 ...

  2. Promise async/await的理解和用法

    Promise && async/await的理解和用法 为什么需要promise(承诺)这个东西 在之前我们处理异步函数都是用回调这个方法,回调嵌套的时候会发现 阅读性 和 调试 的 ...

  3. 学习Promise之前你必须理解的知识点:异步处理的通用模型

    上一篇中提到了回调地狱的问题,ES6为了解决多层回调,提高代码的可阅读性,提出了一套异步的通用模型,这一部分对理解promise很重要 1.两个阶段 该模型中,将一件可能发生异步操作的事情分为两个阶段 ...

  4. Promise Generator Async初步理解(1)

    一.立即执行函数 // 立即执行函数 function getInfo() {return 'I am Zhang'; }function showInfo(param) {console.log(p ...

  5. ES6 Promise的resolved深入理解

    Promise的概念在ES6标准推出来之前已经深入人心,很多框架和第三方库都有类似的实现.但在深入理解ES6的Promise对象的时候,受之前经验的影响,很多概念给人似是而非的感觉,其中有一个特别明显 ...

  6. 【Promise】入门-同步回调-异步回调-JS中的异常error处理-Promis的理解和使用-基本使用-链式调用-七个关键问题

    文章目录 1. 预备知识 1.1 实例对象与函数对象 1.2 两种类型的回调函数 1. 同步回调 2. 异步回调 1.3 JS中的异常error处理 1. 错误的类型 2. 错误处理(捕获与抛出) 3 ...

  7. Javascript 中的神器——Promise

    回调函数真正的问题在于他剥夺了我们使用 return 和 throw 这些关键字的能力.而 Promise 很好地解决了这一切 Promise概念 所谓 Promise,就是ES6原生提供的一个对象, ...

  8. 8张图让你一步步看清 async/await 和 promise 的执行顺序

    2019独角兽企业重金招聘Python工程师标准>>> **摘要:**面试必问 原文:8张图帮你一步步看清 async/await 和 promise 的执行顺序 作者:ziwei3 ...

  9. Promise从入门到精通

    标签: Promise 在ES6之前的JavaScript中处理异步的方法就是使用回调函数,当我们不知道一件事情会在什么时候结束,但是又希望在这件事情结束之后再去做一下其他的操作时,我们在这件事情执行 ...

  10. Java Script 之 Promise

    别说什么promise 是 承诺之类的,我根本听不懂. 作为一个第一次接触的,这么抽象的东西不可能那么简单的第一时间理解透彻. 1,先搞懂啥是异步操作 function callback() { co ...

最新文章

  1. python神秘的魔法函数_Python魔法函数
  2. 给Java新手的一些建议——Java知识点归纳(Java基础部分)
  3. Linux基础命令---lpr
  4. [codevs 1034] 家园
  5. 《炉石传说》架构设计赏析(4):Asset管理
  6. Could not resolve all dependencies for configuration ':app:_debugApk'
  7. com 名字对象(3)使用IMoniker
  8. P1678 烦恼的高考志愿
  9. java mysql 配置_Java连接MySQL数据库详细分析
  10. Tapestry中listener监听方法传递参数的方式
  11. ArcGIS数据编码
  12. 英文名字及意义(超全)
  13. .Net Self Hosting 的几种方式
  14. web前端设计师需要常去的15个设计博客资源站
  15. Python根据地名获取经纬度
  16. JavaScript输出菱形
  17. redis优化,bigkey,hotkey,配置优化,缓存雪崩、缓存穿透、缓存击穿
  18. matlab 的 符号,MATLAB符号计算
  19. HTML5文件夹隐藏了怎么打开,怎样显示隐藏文件夹|win7系统如何显示隐藏文件夹...
  20. 如何用计算机看三维电影,用电脑看3D影片的步骤 电脑怎么设置看3D电影

热门文章

  1. 数字图像处理第一次试验:图像的基本操作和基本统计指标计算
  2. 单片机、ARM与DSP对比
  3. ElasticSearch搜索引擎:数据的写入流程
  4. Winform实现读写IC卡Demo源码含注释
  5. disjoint MATLAB,matlab 计算李雅普诺夫指数的方法集合
  6. 第一次开卡SSD硬盘成功,主控为SM2258XT(附软件)
  7. AGND和GND_SIGNAL之间的隔离_PCB
  8. Linux中tar压缩命令详解
  9. 免费下载百度 豆丁 丁香 等平台文档
  10. 谷歌地球 Google Earth v7.3.2.5495 专业版