Promise.all是什么,如何手动实现Promise.all
Admin 2022-09-06 群英技术资讯 470 次浏览
Promise.all
是 es6 Promise
对象上的一个方法,它的功能就是将多个Promise
实例包装成一个promise
实例。以下是 MDN 对 Promise.all
的描述:
Promise.all() 方法接收一个 promise 的 iterable 类型(注:Array,Map,Set都属于ES6的iterable类型)的输入,并且只返回一个Promise
实例, 那个输入的所有 promise 的 resolve 回调的结果是一个数组。这个Promise
的 resolve 回调执行是在所有输入的 promise 的 resolve 回调都结束,或者输入的 iterable 里没有 promise 了的时候。它的 reject 回调执行是,只要任何一个输入的 promise 的 reject 回调执行或者输入不合法的 promise 就会立即抛出错误,并且reject的是第一个抛出的错误信息。
我戴上我的300度近视眼镜,仔细地提取出这段描述中的关键字:
Promise.all
的返回值是一个新的 Promise
实例。Promise.all
接受一个可遍历的数据容器,容器中每个元素都应是 Promise
实例。咱就是说,假设这个容器就是数组。Promise
实例都成功时(由pendding
状态转化为fulfilled
状态),Promise.all
才成功。这些 Promise
实例所有的 resolve
结果会按照原来的顺序集合在一个数组中作为 Promise.all
的 resolve
的结果。Promise
实例失败(由pendding
状态转化为rejected
状态),Promise.all
就失败。Promise.all
的 .catch()
会捕获到这个 reject
。咱先看看原生的Promise.all
的是啥效果。
const p1 = Promise.resolve('p1')
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p2 延时一秒')
}, 1000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p3 延时两秒')
}, 2000)
})
const p4 = Promise.reject('p4 rejected')
const p5 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('p5 rejected 延时1.5秒')
}, 1500)
})
// 所有Promise实例都成功
Promise.all([p1, p2, p3])
.then(res => {
console.log(res)
})
.catch(err => console.log(err)) // 2秒后打印 [ 'p1', 'p2 延时一秒', 'p3 延时两秒' ]
// 一个Promise实例失败
Promise.all([p1, p2, p4])
.then(res => {
console.log(res)
})
.catch(err => console.log(err)) // p4 rejected
// 一个延时失败的Promise
Promise.all([p1, p2, p5])
.then(res => {
console.log(res)
})
.catch(err => console.log(err)) // 1.5秒后打印 p5 rejected
// 两个Promise实例失败
Promise.all([p1, p4, p5])
.then(res => {
console.log(res)
})
.catch(err => console.log(err)) // p4 rejected
复制代码
注意
上面 p4
和 p5
在未传入 Promise.all
时需要注释掉,因为一个调用了 reject
的 Promise
实例如果没有使用 .catch()
方法去捕获错误会报错。但如果 Promise
实例定义了自己的 .catch
,就不会触发 Promise.all
的 .catch()
方法。
OK,理论存在,实践开始!
Promise.all
接受一个数组,返回值是一个新的 Promise
实例Promise.MyAll = function (promises) {
return new Promise((resolve, reject) => {
})
}
复制代码
Promise
实例都成功,Promise.all
才成功。不难想到,咱得需要一个数组来收集这些 Promise
实例的 resolve
结果。但有句俗话说得好:“不怕一万,就怕万一”,万一数组里面有元素不是 Promise
咋办 —— 那就得用 Promise.resolve()
把它办了。这里还有一个问题,Promise
实例是不能直接调用 resolve
方法的,咱得在 .then()
中去收集结果。注意要保持结果的顺序。Promise.MyAll = function (promises) {
let arr = []
return new Promise((resolve, reject) => {
promises.forEach((item, i) => {
Promise.resolve(item).then(res => {
arr[i] = res
})
})
})
}
复制代码
arr
)作为参数传给外层的 resolve
方法。这里咱们肯定是有一个判断条件的,如何判断所有 Promise
实例都成功了呢?新手容易写出这句代码(没错就是我本人了):if (arr.length === promises.length) resolve(arr)
复制代码
咱仔细想想 Promise
使用来干嘛的 —— 处理异步任务。对呀,异步任务很多都需要花时间呀,如果这些 Promise
中最后一个先完成呢?那 arr
数组不就只有最后一项了,前面的所有项都是 empty
。所以这里咱们应该创建一个计数器,每有一个 Promise
实例成功,计数器加一:
Promise.MyAll = function (promises) {
let arr = [],
count = 0
return new Promise((resolve, reject) => {
promises.forEach((item, i) => {
Promise.resolve(item).then(res => {
arr[i] = res
count += 1
if (count === promises.length) resolve(arr)
})
})
})
}
复制代码
.catch()
方法捕获失败:Promise.MyAll = function (promises) {
let arr = [],
count = 0
return new Promise((resolve, reject) => {
promises.forEach((item, i) => {
Promise.resolve(item).then(res => {
arr[i] = res
count += 1
if (count === promises.length) resolve(arr)
}).catch(reject)
})
})
}
复制代码
第二种写法就是给 .then()
方法传入第二个参数,这个函数是处理错误的回调函数:
Promise.MyAll = function (promises) {
let arr = [],
count = 0
return new Promise((resolve, reject) => {
promises.forEach((item, i) => {
Promise.resolve(item).then(res => {
arr[i] = res
count += 1
if (count === promises.length) resolve(arr)
}, reject)
})
})
}
复制代码
致此 Promise.all
大功告成,赶紧拿来测试一下(摩拳擦掌):
const p1 = Promise.resolve('p1')
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p2 延时一秒')
}, 1000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p3 延时两秒')
}, 2000)
})
const p4 = Promise.reject('p4 rejected')
const p5 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('p5 rejected 延时1.5秒')
}, 1500)
})
// 所有 Promsie 都成功
Promise.MyAll([p1, p2, p3])
.then(res => console.log(res))
.catch(err => console.log(err)) // 2秒后打印 [ 'p1', 'p2 延时一秒', 'p3 延时两秒' ]
// 一个 Promise 失败
Promise.MyAll([p1, p2, p4])
.then(res => console.log(res))
.catch(err => console.log(err)) // p4 rejected
// 一个延时失败的 Promise
Promise.MyAll([p1, p2, p5])
.then(res => console.log(res))
.catch(err => console.log(err)) // 1.5秒后打印 p5 rejected 延时1.5秒
// 两个失败的 Promise
Promise.MyAll([p1, p4, p5])
.then(res => console.log(res))
.catch(err => console.log(err)) // p4 rejected
复制代码
“OhOhOhOh~~~~”,与原生的 Promise.all
运行结果不能说很像,只能说一模一样。老话说的好,趁热打铁——正在火候上。我打开某个学习网站(MDN Web Docs (mozilla.org)),了解到 Promise
对象用于同时处理多个 Promise
的方法还有 Promise.race
、Promise.any
、Promise.allSettle
。从小老师就教会了咱们举一反三,仔细看了这三个方法的描述之后,我还真给反出来了。
Promise.race
从字面意思理解就是赛跑,以状态变化最快的那个 Promise
实例为准,最快的 Promise
成功 Promise.race
就成功,最快的 Promise
失败 Promise.race
就失败。
咱来看看原生 Promise.race
效果
const p1 = Promise.resolve('p1')
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p2 延时一秒')
}, 1000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p3 延时两秒')
}, 2000)
})
const p4 = Promise.reject('p4 rejected')
const p5 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('p5 rejected 延时1秒')
}, 1500)
})
// p1无延时,p2延时1s,p3延时2s
Promise.race([p1, p2, p3])
.then(res => console.log(res))
.catch(err => console.log(err)) // p1
// p4无延时reject
Promise.race([p4, p2, p3])
.then(res => console.log(res))
.catch(err => console.log(err)) // p4 rejected
// p5 延时1.5秒reject,p2延时1s
Promise.race([p5, p2, p3])
.then(res => console.log(res))
.catch(err => console.log(err)) // 1s后打印: p2 延时一秒
复制代码
理论存在,实践开始
整体流程与 Promise
差不多,只是对数组中的 Promise
实例处理的逻辑不一样,这里我们需要将最快改变状态的 Promise
结果作为 Promise.race
的结果,相对来说就比较简单了,代码如下:
Promise.MyRace = function (promises) {
return new Promise((resolve, reject) => {
// 这里不需要使用索引,只要能循环出每一项就行
for (const item of promises) {
Promise.resolve(item).then(resolve, reject)
}
})
}
复制代码
还是刚才几个案例,咱就不重复写了
// p1无延时,p2延时1s,p3延时2s
Promise.MyRace([p1, p2, p3])
.then(res => console.log(res))
.catch(err => console.log(err)) // p1
// p4无延时reject
Promise.MyRace([p4, p2, p3])
.then(res => console.log(res))
.catch(err => console.log(err)) // p4 rejected
// p5 延时1.5秒reject,p2延时1s
Promise.MyRace([p5, p2, p3])
.then(res => console.log(res))
.catch(err => console.log(err)) // 1s后打印: p2 延时一秒
复制代码
可以看到,结果与原生的 Promise.race
是一致的,成功!
Promise.any
与 Promise.all
可以看做是相反的。Promise.any
中只要有一个 Promise
实例成功就成功,只有当所有的 Promise
实例失败时 Promise.any
才失败,此时Promise.any
会把所有的失败/错误集合在一起,返回一个失败的 promise
和AggregateError
类型的实例。MDN 上说这个方法还处于试验阶段,如果 node
或者浏览器版本过低可能无法使用,各位看官自行测试下。
const p1 = Promise.resolve('p1')
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p2 延时一秒')
}, 1000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p3 延时两秒')
}, 2000)
})
const p4 = Promise.reject('p4 rejected')
const p5 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('p5 rejected 延时1.5秒')
}, 1500)
})
// 所有 Promise 都成功
Promise.any([p1, p2, p3])
.then(res => console.log(res))
.catch(err => console.log(err)) // p1
// 两个 Promise 成功
Promise.any([p1, p2, p4])
.then(res => console.log(res))
.catch(err => console.log(err)) // p1
// 只有一个延时成功的 Promise
Promise.any([p2, p4, p5])
.then(res => console.log(res))
.catch(err => console.log(err)) // p2 延时1秒
// 所有 Promise 都失败
Promise.any([p4, p5])
.then(res => console.log(res))
.catch(err => console.log(err)) // AggregateError: All promises were rejected
复制代码
可以看出,如果 Promise.any
中有多个成功的 Promise
实例,则以最快成功的那个结果作为自身 resolve
的结果。
OK,理论存在,实践开始
Promise.any
的整体结构:Promise.MyAny = function (promises) {
return new Promise((resolve, reject) => {
promises.forEach((item, i) => {
})
})
}
复制代码
Promise.all
的逻辑是反的,咱们需要收集 reject
的 Promise
,也需要一个数组和计数器,用计数器判断是否所有的 Promise
实例都失败。另外在收集失败的 Promise
结果时咱需要打上一个失败的标记方便分析结果。Promise.MyAny = function (promises) {
let arr = [],
count = 0
return new Promise((resolve, reject) => {
promises.forEach((item, i) => {
Promise.resolve(item).then(resolve, err => {
arr[i] = { status: 'rejected', val: err }
count += 1
if (count === promises.length) reject(new Error('没有promise成功'))
})
})
})
}
复制代码
这里我没有使用 MDN 上规定的 AggregateError
实例,手写嘛,随心所欲一点,写自己看着舒服的
// 所有 Promise 都成功
Promise.MyAny([p1, p2, p3])
.then(res => console.log(res))
.catch(err => console.log(err)) // p1
// 两个 Promise 成功
Promise.MyAny([p1, p2, p4])
.then(res => console.log(res))
.catch(err => console.log(err)) // p1
// 只有一个延时成功的 Promise
Promise.MyAny([p2, p4, p5])
.then(res => console.log(res))
.catch(err => console.log(err)) // p2 延时1秒
// 所有 Promise 都失败
Promise.MyAny([p4, p5])
.then(res => console.log(res))
.catch(err => console.log(err)) // 没有promise成功
复制代码
有时候,咱代码人总是会有点特殊的需求:如果咱希望一组 Promise
实例无论成功与否,都等它们异步操作结束了在继续执行下一步操作,这可如何是好?于是就出现了 Promise.allSettled
。
Promise.allSettled
测试const p1 = Promise.resolve('p1')
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p2 延时一秒')
}, 1000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('p3 延时两秒')
}, 2000)
})
const p4 = Promise.reject('p4 rejected')
const p5 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('p5 rejected 延时1.5秒')
}, 1500)
})
// 所有 Promise 实例都成功
Promise.allSettled([p1, p2, p3])
.then(res => console.log(res))
.catch(err => console.log(err))
// [
// { status: 'fulfilled', value: 'p1' },
// { status: 'fulfilled', value: 'p2 延时一秒' },
// { status: 'fulfilled', value: 'p3 延时两秒' }
// ]
// 有一个 Promise 失败
Promise.allSettled([p1, p2, p4])
.then(res => console.log(res))
.catch(err => console.log(err))
// [
// { status: 'fulfilled', value: 'p1' },
// { status: 'fulfilled', value: 'p2 延时一秒' },
// { status: 'rejected' , value: 'p4 rejected' }
// ]
// 所有 Promise 都失败
Promise.allSettled([p4, p5])
.then(res => console.log(res))
.catch(err => console.log(err))
// [
// { status: 'rejected', reason: 'p4 rejected' },
// { status: 'rejected', reason: 'p5 rejected 延时1.5秒' }
// ]
复制代码
可以看到,与 Promise.any
类似,Promise.allSettled
也给所有收集到的结果打上了标记。而且 Promise.allSettled
是不会变成 rejected
状态的,不管一组 Promise
实例的各自结果如何,Promise.allSettled
都会转变为 fulfilled
状态。
OK,理论存在,实践开始
咱就是说,得用个数组把所有的 Promise
实例的结果(无论成功与否)都收集起来,判断收集完了(所有 Promise
实例状态都改变了),咱就将这个收集到的结果 resolve
掉。收集成功 Promise
结果的逻辑咱们在 Promise.all
中实现过,收集失败 Promise
结果咱们在 Promise.any
中处理过。这波,这波是依葫芦画瓢——照样。
Promise.MyAllSettled = function (promises) {
let arr = [],
count = 0
return new Promise((resolve, reject) => {
promises.forEach((item, i) => {
Promise.resolve(item).then(res => {
arr[i] = { status: 'fulfilled', val: res }
count += 1
if (count === promises.length) resolve(arr)
}, (err) => {
arr[i] = { status: 'rejected', val: err }
count += 1
if (count === promises.length) resolve(arr)
})
})
})
}
复制代码
这代码,逻辑上虽说没问题,但各位优秀的程序员们肯定是看不顺眼的,怎么会有两段重复的代码捏,不行,咱得封装一下。
Promise.MyAllSettled = function (promises) {
let arr = [],
count = 0
return new Promise((resolve, reject) => {
const processResult = (res, index, status) => {
arr[index] = { status: status, val: res }
count += 1
if (count === promises.length) resolve(arr)
}
promises.forEach((item, i) => {
Promise.resolve(item).then(res => {
processResult(res, i, 'fulfilled')
}, err => {
processResult(err, i, 'rejected')
})
})
})
}
复制代码
perfect,俗话说得好:没病走两步。老样子,给代码跑几个案例。
// 所有 Promise 实例都成功
Promise.MyAllSettled([p1, p2, p3])
.then(res => console.log(res))
.catch(err => console.log(err))
// [
// { status: 'fulfilled', value: 'p1' },
// { status: 'fulfilled', value: 'p2 延时一秒' },
// { status: 'fulfilled', value: 'p3 延时两秒' }
// ]
// 有一个 MyAllSettled 失败
Promise.allSettled([p1, p2, p4])
.then(res => console.log(res))
.catch(err => console.log(err))
// [
// { status: 'fulfilled', value: 'p1' },
// { status: 'fulfilled', value: 'p2 延时一秒' },
// { status: 'rejected' , value: 'p4 rejected' }
// ]
// 所有 MyAllSettled 都失败
Promise.allSettled([p4, p5])
.then(res => console.log(res))
.catch(err => console.log(err))
// [
// { status: 'rejected', reason: 'p4 rejected' },
// { status: 'rejected', reason: 'p5 rejected 延时1.5秒' }
// ]
复制代码
致此,大功告成,我可以骄傲地对妈妈说:“妈妈,我再也不怕 Promise.all”了
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
newArray(arg1,arg2,…),当参数长度为0或大于等于2时,传入的参数将依次成为新数组的第0至第N项。newArray(len),当len不是数值时,返回一个只包含len元素的数组。
JavaScript中如何实现清除session?在实际的项目中,清除session的需求是常见的,这篇就给大家来分享一下如何实现当前页面并清除session,实现代码如下,感兴趣的朋友可以参考,接下来就跟随小编一起了解看看吧。
本篇文章给大家带来了关于javascript的相关知识,其中主要介绍了关于旋转数组的相关问题,包括了什么是旋转数组、环状替换等等内容,下面一起来看一下,希望对大家有帮助。
这篇文章我们主要了解JS与CSS阻塞的问题,我们知道JS与CSS中都阻塞,但是阻塞不同,JS阻塞DOM树的构建,CSS阻塞页面的显示,接下来我们就详细的了解一下。
用jquery实现省市区联动的方法:1、创建一个HTML示例文件;2、在js文件中通过“$(function() {for(var i = 0; i < prvo.length; i++) {...}}”方法实现省市区联动即可。
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008