Short-circuit? | Short-circuits on? | Fulfilled on? | Rejected on? | |
---|---|---|---|---|
Promise.all | Yes | First rejected promise | All promise fulfilled | First rejected promise |
Promise.allSettled | No | N/A | Always | N/A |
Promise.race | Yes | First settled | First promise fulfilled | First rejected promise |
Promise.any | Yes | First fulfilled | First promise fulfilled | All rejected promises |
Promise.race 与 Promise.any 以及 Promise.all 与 Promise.allSettled
2019 年 5 月 8 日的JavaScript 新功能(Google I/O '19)展示了静态 Promise 组合器方法Promise.allSettled和Promise.any的新功能。
现代浏览器中已经有两种可用的方法,Promise.all和Promise.race。
让我们看看它们之间的差异以及每种方法的工作原理。
🚀 先决条件
🔆 Promise 定义
我将跳过什么是承诺,直接进入静态方法并讨论差异。
要点是,承诺是 JavaScript 向您承诺某项工作将会完成(如果工作无法完成则可能会失败)的方式。
如果您熟悉 C#,它类似于 Task 类。
欲了解更多信息,请参阅以下文档。
- Promise – MDN 上的JavaScript
- JavaScript Promises: Google Developers简介
🔆 Promise 状态定义
- 已实现——当承诺成功实现时。
- 拒绝——当承诺失败时。
- 待定——当承诺“既未实现也未拒绝”时。
- 已解决——实际上不是一种状态,而是一个总称,用来描述承诺已实现或被拒绝。
- 该术语稍后将用于描述新方法的特点。
有关状态和命运的更详细说明,请参阅状态和命运。
还有其他静态 Promise 方法,例如Promise.reject、Promise.resolve,但我将仅介绍“组合器”方法,它以可迭代对象作为参数。
🚀 差异
让我们首先看一下现有组合器方法和新组合器方法之间的区别。
🔅 Promise.all 与 Promise.allSettled
两者都接受可迭代对象,但是
Promise.all
一旦可迭代对象内的承诺被拒绝,就会拒绝。Promise.allSettled
无论可迭代对象内的承诺是否被拒绝,都会得到解决。
🔅 Promise.race 与 Promise.any
两者都接受可迭代对象,但是
Promise.race
可迭代对象中第一个已解决(已履行或已拒绝)的承诺发生短路。Promise.any
在第一个实现的承诺上短路,并继续解决,无论承诺是否被拒绝,除非可迭代对象内的所有承诺都被拒绝。
🚀 比较表
现在让我们看一下现有/即将推出的组合器方法。
Short-circuit? | Short-circuits on? | Fulfilled on? | Rejected on? | |
---|---|---|---|---|
Promise.all | Yes | First rejected promise | All promise fulfilled | First rejected promise |
Promise.allSettled | No | N/A | Always | N/A |
Promise.race | Yes | First settled | First promise fulfilled | First rejected promise |
Promise.any | Yes | First fulfilled | First promise fulfilled | All rejected promises |
现在让我们继续了解每种方法的更多信息。
请注意,所有“特性”均取自 TC39 提案README。
🚀 Promise.all
- 这是什么?将所有传递的 Promise 解析为可迭代对象。
- 成语——一桩坏事🍏会毁掉一堆(“全部”)。
- 特性——当输入值被拒绝时短路
🔆示例
const promisesWithoutReject = [ | |
Promise.resolve('🍎 #1'), | |
'🍎 #2', | |
new Promise((resolve, reject) => setTimeout(resolve, 100, '🍎 #3')) | |
] | |
Promise.all(promisesWithoutReject) | |
.then(apples => console.log(`We can sell all these good apples`, apples)) | |
const promisesWithOneReject = [ | |
Promise.resolve('🍎 #1'), | |
'🍎 #2', | |
new Promise((_, reject) => setTimeout(reject, 100, 'Bad 🍏')) | |
] | |
Promise.all(promisesWithOneReject) | |
.then(console.log) | |
.catch(badApple => | |
console.error(`Threw out all apples because of this`, badApple)) |
We can sell all these good apples [ '🍎 #1', '🍎 #2', '🍎 #3' ] | |
Threw out all apples because of this Bad 🍏 |
const promisesWithoutReject = [ | |
Promise.resolve('🍎 #1'), | |
'🍎 #2', | |
new Promise((resolve, reject) => setTimeout(resolve, 100, '🍎 #3')) | |
] | |
Promise.all(promisesWithoutReject) | |
.then(apples => console.log(`We can sell all these good apples`, apples)) | |
const promisesWithOneReject = [ | |
Promise.resolve('🍎 #1'), | |
'🍎 #2', | |
new Promise((_, reject) => setTimeout(reject, 100, 'Bad 🍏')) | |
] | |
Promise.all(promisesWithOneReject) | |
.then(console.log) | |
.catch(badApple => | |
console.error(`Threw out all apples because of this`, badApple)) |
We can sell all these good apples [ '🍎 #1', '🍎 #2', '🍎 #3' ] | |
Threw out all apples because of this Bad 🍏 |
当Promise.all
fulfilled( promisesWithoutReject
) 时,所有苹果都会被归还。
后一个例子promisesWithOneReject
表明一个被拒绝的承诺会导致所有承诺都被拒绝。
🚀 Promise.allSettled
- 这是什么?所有承诺,无论其状态如何(已完成/拒绝)。
- 成语——让我们“拭目以待”🤔。
- 特点– 与 Promise.all/race 不同,不会短路
- 注意– 可在Chrome 76中使用。
🔆示例
const allRejectedPromises = [ | |
Promise.reject('🍏 #1'), | |
Promise.reject('🍏 #2'), | |
Promise.reject('🍏 #3'), | |
] | |
Promise.allSettled(allRejectedPromises) | |
.then(badApples => | |
console.log(`We can't sell any of these apples...`, badApples)) | |
.catch(error => console.error('This should never occur')) | |
const promisesWithoutReject = [ | |
Promise.resolve('🍎 #1'), | |
'🍎 #2', | |
new Promise((resolve, reject) => setTimeout(resolve, 100, '🍎 #3')) | |
] | |
Promise.allSettled(promisesWithoutReject) | |
.then(apples => console.log(`We can sell all these good apples`, apples.map(_=>_.value))) | |
const promisesWithOneReject = [ | |
Promise.resolve('🍎 #1'), | |
new Promise((_, reject) => setTimeout(reject, 10, '🍏 #2')), | |
'🍎 #3', | |
new Promise((_, reject) => setTimeout(reject, 100, '🍏 #4')) | |
] | |
Promise.allSettled(promisesWithOneReject) | |
.then(apples => { | |
const badApples = apples.filter(apple => apple.status === 'rejected').map(_ => _.reason) | |
const goodApples = apples.filter(apple => apple.status === 'fulfilled').map(_ => _.value) | |
console.log(`Let's throw out`, badApples, `and sell the rest`, goodApples) | |
}) |
We can't sell any of these apples... | |
[ { status: 'rejected', reason: '🍏 #1' }, | |
{ status: 'rejected', reason: '🍏 #2' }, | |
{ status: 'rejected', reason: '🍏 #3' } ] | |
We can sell all these good apples [ '🍎 #1', '🍎 #2', '🍎 #3' ] | |
Let's throw out [ '🍏 #2', '🍏 #4' ] and sell the rest [ '🍎 #1', '🍎 #3' ] |
const allRejectedPromises = [ | |
Promise.reject('🍏 #1'), | |
Promise.reject('🍏 #2'), | |
Promise.reject('🍏 #3'), | |
] | |
Promise.allSettled(allRejectedPromises) | |
.then(badApples => | |
console.log(`We can't sell any of these apples...`, badApples)) | |
.catch(error => console.error('This should never occur')) | |
const promisesWithoutReject = [ | |
Promise.resolve('🍎 #1'), | |
'🍎 #2', | |
new Promise((resolve, reject) => setTimeout(resolve, 100, '🍎 #3')) | |
] | |
Promise.allSettled(promisesWithoutReject) | |
.then(apples => console.log(`We can sell all these good apples`, apples.map(_=>_.value))) | |
const promisesWithOneReject = [ | |
Promise.resolve('🍎 #1'), | |
new Promise((_, reject) => setTimeout(reject, 10, '🍏 #2')), | |
'🍎 #3', | |
new Promise((_, reject) => setTimeout(reject, 100, '🍏 #4')) | |
] | |
Promise.allSettled(promisesWithOneReject) | |
.then(apples => { | |
const badApples = apples.filter(apple => apple.status === 'rejected').map(_ => _.reason) | |
const goodApples = apples.filter(apple => apple.status === 'fulfilled').map(_ => _.value) | |
console.log(`Let's throw out`, badApples, `and sell the rest`, goodApples) | |
}) |
We can't sell any of these apples... | |
[ { status: 'rejected', reason: '🍏 #1' }, | |
{ status: 'rejected', reason: '🍏 #2' }, | |
{ status: 'rejected', reason: '🍏 #3' } ] | |
We can sell all these good apples [ '🍎 #1', '🍎 #2', '🍎 #3' ] | |
Let's throw out [ '🍏 #2', '🍏 #4' ] and sell the rest [ '🍎 #1', '🍎 #3' ] |
无论已解决(已实现或已拒绝)状态如何,所有承诺都会得到解决,而不会短路catch
。
为了区分解析值是否成功,它们将作为以下形状的对象数组返回。
- 已履行的承诺将返回为
{status: 'fulfilled', value}
- 被拒绝的承诺将返回为
{status: 'rejected', reason}
🚀 Promise.race
- 这是什么?第一个完成的 Promise,或者即使有一个 Promise 被拒绝,也拒绝整个 Promise。
- 成语– 善与恶之间的竞赛 😇]( https://sworg/images/core/emoji/12.0.0-1/72x72/1f607.png ) (已实现) 与 ![😈 (已拒绝)
- 虽然这不算是一个成语😅
- 特性– 输入值稳定时短路
🔆示例
const promiseWillFulfill = [ | |
new Promise((resolve, reject) => setTimeout(reject, 250, '😈')), | |
new Promise((resolve, reject) => setTimeout(resolve, 150, '😇')), | |
new Promise((resolve, reject) => setTimeout(resolve, 1, '😇')), | |
] | |
Promise.race(promiseWillFulfill) | |
.then(value => console.log(`The humanity survives "${value}"`)) | |
.catch(error => console.log(`Won't be called as 😇 will win the race`)) | |
const promiseWillReject = [ | |
new Promise((resolve, reject) => setTimeout(resolve, 250, '😇')), | |
new Promise((resolve, reject) => setTimeout(reject, 1, '😈')), | |
new Promise((resolve, reject) => setTimeout(resolve, 250, '😇')), | |
] | |
Promise.race(promiseWillReject) | |
.then(value => console.log(`This won't be called...="${value}"`)) | |
.catch(error => console.log(`The humanity is doomed...="${error}"`)) | |
const promisesWithOUTReject = [ | |
new Promise(resolve => setTimeout(resolve, 350, 'one')), | |
new Promise(resolve => setTimeout(resolve, 250, 'two')), | |
new Promise(resolve => setTimeout(resolve, 150, 'three')), | |
] | |
Promise.race(promisesWithOUTReject) | |
.then(value => console.log(`Promise without reject="${value}"`)) |
The humanity survives "😇" | |
The humanity is doomed...="😈" | |
Promise without reject="three" |
const promiseWillFulfill = [ | |
new Promise((resolve, reject) => setTimeout(reject, 250, '😈')), | |
new Promise((resolve, reject) => setTimeout(resolve, 150, '😇')), | |
new Promise((resolve, reject) => setTimeout(resolve, 1, '😇')), | |
] | |
Promise.race(promiseWillFulfill) | |
.then(value => console.log(`The humanity survives "${value}"`)) | |
.catch(error => console.log(`Won't be called as 😇 will win the race`)) | |
const promiseWillReject = [ | |
new Promise((resolve, reject) => setTimeout(resolve, 250, '😇')), | |
new Promise((resolve, reject) => setTimeout(reject, 1, '😈')), | |
new Promise((resolve, reject) => setTimeout(resolve, 250, '😇')), | |
] | |
Promise.race(promiseWillReject) | |
.then(value => console.log(`This won't be called...="${value}"`)) | |
.catch(error => console.log(`The humanity is doomed...="${error}"`)) | |
const promisesWithOUTReject = [ | |
new Promise(resolve => setTimeout(resolve, 350, 'one')), | |
new Promise(resolve => setTimeout(resolve, 250, 'two')), | |
new Promise(resolve => setTimeout(resolve, 150, 'three')), | |
] | |
Promise.race(promisesWithOUTReject) | |
.then(value => console.log(`Promise without reject="${value}"`)) |
The humanity survives "😇" | |
The humanity is doomed...="😈" | |
Promise without reject="three" |
例如promiseWillFulfill
,第一个承诺在 1 毫秒内实现,因此人类得以幸存。
但第二个例子使用的promiseWillReject
承诺在 1 毫秒内被拒绝,因此人类注定要失败。
最后一个例子( )没有被拒绝就完成了,因此 返回了promisesWithOUTReject
第一个完成的承诺值“三”。
从这些例子中,你可以看到第一个确定的状态(履行或拒绝)使承诺短路。
🚀 Promise.any
- 这是什么?无论其他 Promise 是否被拒绝,都返回第一个已完成的 Promise。如果所有 Promise 都被拒绝,则通过为所有被拒绝的 Promise 提供错误信息来拒绝。
- 成语——结果好,一切都好。
- 特性——当输入值满足时短路。
- 注意– 尚未在任何浏览器中实现,并且处于第 1 阶段。
🔆示例
// Example #1 | |
Promise.any([ | |
Promise.reject('✗'), | |
Promise.reject('✗'), | |
Promise.resolve('✓'), | |
Promise.reject('✗'), | |
]).then(function(value) { | |
console.log(`You win at life`, value) | |
}); | |
// Example #2 | |
// You get the first fulfilled value | |
Promise.any([ | |
new Promise((_, reject) => setTimeout(reject, 10, '✗')), | |
new Promise((_, reject) => setTimeout(reject, 20, '✗')), | |
new Promise((_, reject) => setTimeout(reject, 30, '✗')), | |
new Promise(resolve => setTimeout(resolve, 100, 'I got a job!')), | |
new Promise(resolve => setTimeout(resolve, 1000, 'I could have gotten a better job!')), | |
]).then(function(value) { | |
console.log(value) | |
}); | |
// Example #3 | |
// You get all rejection reasons | |
Promise.any([ | |
Promise.reject('✗'), | |
Promise.reject('✗'), | |
]).catch(function(reasons) { | |
console.log(`Didn't get any offers...`, reasons) | |
}); |
// Note that the result of Example #3 is printed first because of `setTimeout` in example #2 | |
// Result of Example #1 | |
You win at life ✓ | |
// Result of Example #3 | |
Didn't get any offers... [ '✗', '✗' ] | |
// Result of Example #2 | |
I got a job! |
// Example #1 | |
Promise.any([ | |
Promise.reject('✗'), | |
Promise.reject('✗'), | |
Promise.resolve('✓'), | |
Promise.reject('✗'), | |
]).then(function(value) { | |
console.log(`You win at life`, value) | |
}); | |
// Example #2 | |
// You get the first fulfilled value | |
Promise.any([ | |
new Promise((_, reject) => setTimeout(reject, 10, '✗')), | |
new Promise((_, reject) => setTimeout(reject, 20, '✗')), | |
new Promise((_, reject) => setTimeout(reject, 30, '✗')), | |
new Promise(resolve => setTimeout(resolve, 100, 'I got a job!')), | |
new Promise(resolve => setTimeout(resolve, 1000, 'I could have gotten a better job!')), | |
]).then(function(value) { | |
console.log(value) | |
}); | |
// Example #3 | |
// You get all rejection reasons | |
Promise.any([ | |
Promise.reject('✗'), | |
Promise.reject('✗'), | |
]).catch(function(reasons) { | |
console.log(`Didn't get any offers...`, reasons) | |
}); |
// Note that the result of Example #3 is printed first because of `setTimeout` in example #2 | |
// Result of Example #1 | |
You win at life ✓ | |
// Result of Example #3 | |
Didn't get any offers... [ '✗', '✗' ] | |
// Result of Example #2 | |
I got a job! |
第一个例子是承诺立即被拒绝,但并没有因为履行承诺而短路,因此你在生活中获胜。
第二个例子是 Promise 在一段时间后会 resolve。第一个 fulfilled 的 Promise 是在一系列拒绝之后 resolve 的,但没有短路。这样你就找到了一份工作。
当所有承诺都被拒绝时,Promise.any 也会被拒绝,并且你不会收到任何工作机会。
👋 结论
我的理解是,新Promise.allSettled/any
引入的 Promise 会尽力解决要履行的承诺,而不像现有的承诺那样在第一次遇到拒绝时就失败。
Promise.all
&Promise.race
已在现代浏览器中可用(不包括 IE ;p),并将Promise.allSettled
在 Chrome 76 中可用。
Promise.any
仍处于第 1 阶段,尚未在任何浏览器中使用(但可在Bluebird中使用或使用 polyfills - 对于演示,我使用promise-any NPM 库进行演示。)
我很想知道你在哪里会用(曾经用过)每种方法来解决问题。
如果你发现任何错误,或者我该如何改进这个例子,请告诉我好吗?
Promise.race 与 Promise.any 以及 Promise.all 与 Promise.allSettled 的文章最先出现在Sung 的技术博客上。
文章来源:https://dev.to/dance2die/promise-race-vs-promise-any-and-promise-all-vs-promise-allsettled-26if