时间:2021-05-18
异步之Promise
Promise.all
Promise.all接收的promise数组是按顺序执行的还是一起执行的,也就是说返回的结果是顺序固定的吗?
目前有两种答案:
那么我们根据实现来解密:
环境为:
vscode 1.20.1node v8.9.0npm v5.6.0实验代码:
// 获取随机数,toFixed为四舍五入保留小数,0为保留整数,范围~1000const 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。我们就要找出这个原因,那就是找到了为什么要使用箭头函数。
首先我通过调试来查找
如图:
程序首先打印出了
taskID=1 start.
taskID=2 start.
taskID=3 start.
说明一定是先执行了
console.log(`taskID=${taskID} start.`);所以我们在这段打上断点进行一步一步调试,如下:
根据上图我们可以看出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);});以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
JavaScript的Promise.all()Promise是JavaScript异步编程的一种解决方案,在ES6中引入。通过Promise.all()可以实
在js里面,偶尔会遇见需要多个异步按照顺序执行请求,又不想多层嵌套,,这里和promise.all的区别在于,promise或者Jquery里面的$.when是
next只能調用一次,這邊可以用Promise.all解決,等待兩個異步操作都返回結果後再next:beforeRouteEnter(to,from,next)
扩展阅读c#基础系列1---深入理解值类型和引用类型c#基础系列2---深入理解String引言在上篇文章深入理解值类型和引用类型的时候,有的小伙伴就推荐说一说
首先看我们的源代码。复制代码代码如下:深入理解Javascriptconsole.log(this);深入理解Javascript我们知道,通过浏览器打开这个页