为什么 Promise.any() 使用被拒绝承诺的 catch 处理程序的返回值来解决,而不是等待第一个履行的承诺?
Why does Promise.any() resolve with the returned value of the catch handler of a rejected promise instead of waiting for the first fulfilled one?
请考虑以下代码:
const race = () => Promise.any([
// first promise, rejects in 100
new Promise((resolve, reject) => {
setTimeout(reject, 100, 1);
})
.then(r => (console.log('one resolved with', r), r))
.catch(err => (console.warn('one rejected with', err), err)),
// second promise, resolves in 200
new Promise((resolve, reject) => {
setTimeout(resolve, 200, 2);
})
.then(r => (console.log('two resolved with', r), r))
.catch(err => (console.warn('two rejected with', err), err))
])
.then(r => console.log('race resolved with', r))
.catch(err => console.warn('race rejected with', err))
race()
注意 为了简洁起见,我使用了逗号表达式。如果您更喜欢更传统的语法,这里是长格式:
const race = () => Promise.any([
new Promise((resolve, reject) => {
setTimeout(reject, 100, 1);
})
.then(r => {
console.log('one resolved with', r);
return r;
})
.catch(err => {
console.warn('one rejected with', err);
return err;
}),
new Promise((resolve, reject) => {
setTimeout(resolve, 200, 2);
})
.then(r => {
console.log('two resolved with', r);
return r;
})
.catch(err => {
console.warn('two rejected with', err);
return err;
})
])
.then(r => console.log('race resolved with', r))
.catch(err => console.warn('race rejected with', err))
race()
我期待 race()
在 200
中以 2
解决。相反,它在 100
中使用第一个 promise 的 catch
处理程序 (1
) 的 return
值解析(即使它不是显式的——例如:如果我的 catch 会实际上 return 错误。.any()
包装器仍将使用 undefined
解决,而不是等待第一个已履行的承诺。解决,甚至不拒绝!)。
更有趣的是,如果我没有在失败的承诺中指定 catch
处理程序,预期的行为 确实会发生 。看看:
const race = () => Promise.any([
new Promise((resolve, reject) => {
setTimeout(reject, 100, 1);
})
.then(r => (console.log('one resolved with', r), r)),
new Promise((resolve, reject) => {
setTimeout(resolve, 200, 2);
})
.then(r => (console.log('two resolved with', r), r))
])
.then(r => console.log('race resolved with', r))
.catch(err => console.warn('race rejected with', err))
race()
也许我想记录失败!为什么在拒绝时有一些代码 运行 会使父级 Promise.any()
解析为第一个失败承诺的 return 值,该承诺恰好有一个 .catch
处理程序, 而不是等待第一个实现的承诺, as advertised?
无论是否定义 catch
回调,父 Promise.any()
都不应该有相同的行为吗?
这是一个错误吗?我觉得这令人难以置信。
更新:在收到关于为什么会发生这种情况的解释后,我设法获得了我最初寻找的东西(在 catch 块中记录错误,同时仍在等待其他承诺中的第一个实现),通过 returning Promise.reject(err)
在捕获处理程序中:
const race = () => Promise.any([
// first promise, rejects in 100
new Promise((resolve, reject) => {
setTimeout(reject, 100, 1);
})
.then(r => (console.log('one resolved with', r), r))
.catch(err => (console.warn('one rejected with', err), Promise.reject(err))),
// second promise, resolves in 200
new Promise((resolve, reject) => {
setTimeout(resolve, 200, 2);
})
.then(r => (console.log('two resolved with', r), r))
.catch(err => (console.warn('two rejected with', err), Promise.reject(err)))
])
.then(r => console.log('race resolved with', r))
.catch(err => console.warn('race rejected with', err))
race()
当您将 .catch
链接到一个 Promise 上时,整个表达式将计算为已解决的 Promise,该 Promise 的计算结果为 returned 在 .catch
末尾的值。看看下面如何不会导致第二个 .catch
被输入。
Promise.reject()
.catch((err) => { console.log('first catch'); })
.catch((err) => { console.log('second catch'); })
// In other words, the whole below expression resolves to a resolved Promise:
// Promise.reject()
// .catch((err) => { console.log('first catch'); })
整体来说
somePromise
.catch(hander)
为了导致被拒绝的 Promise,handler
必须抛出(或构造和 return 一个被拒绝的 Promise)。
因此,在您的原始代码中:
.catch(err => (console.warn('one rejected with', err), err)),
这使整个表达得到解决。如果你想让它拒绝,把它改成:
.catch(err => {
console.warn('one rejected with', err);
throw err;
}),
(throw
需要独立语句,不能和逗号一起使用)
const race = () => Promise.any([
// first promise, rejects in 100
new Promise((resolve, reject) => {
setTimeout(reject, 100, 1);
})
.then(r => (console.log('one resolved with', r), r))
.catch(err => {
console.warn('one rejected with', err);
throw err;
}),
// second promise, resolves in 200
new Promise((resolve, reject) => {
setTimeout(resolve, 200, 2);
})
.then(r => (console.log('two resolved with', r), r))
.catch(err => (console.warn('two rejected with', err), err))
])
.then(r => console.log('race resolved with', r))
.catch(err => console.warn('race rejected with', err))
race()
解决类似问题的常见方法是 而不是 .catch
除非你可以对错误做一些有用的事情——如果不行,就让被拒绝的 Promise渗透回其调用者,以便其调用者可以处理它。虽然您可以 .catch
并重新抛出,但它有点难看,所以除非必要,否则您可能不想这样做。
请考虑以下代码:
const race = () => Promise.any([
// first promise, rejects in 100
new Promise((resolve, reject) => {
setTimeout(reject, 100, 1);
})
.then(r => (console.log('one resolved with', r), r))
.catch(err => (console.warn('one rejected with', err), err)),
// second promise, resolves in 200
new Promise((resolve, reject) => {
setTimeout(resolve, 200, 2);
})
.then(r => (console.log('two resolved with', r), r))
.catch(err => (console.warn('two rejected with', err), err))
])
.then(r => console.log('race resolved with', r))
.catch(err => console.warn('race rejected with', err))
race()
注意 为了简洁起见,我使用了逗号表达式。如果您更喜欢更传统的语法,这里是长格式:
const race = () => Promise.any([
new Promise((resolve, reject) => {
setTimeout(reject, 100, 1);
})
.then(r => {
console.log('one resolved with', r);
return r;
})
.catch(err => {
console.warn('one rejected with', err);
return err;
}),
new Promise((resolve, reject) => {
setTimeout(resolve, 200, 2);
})
.then(r => {
console.log('two resolved with', r);
return r;
})
.catch(err => {
console.warn('two rejected with', err);
return err;
})
])
.then(r => console.log('race resolved with', r))
.catch(err => console.warn('race rejected with', err))
race()
我期待 race()
在 200
中以 2
解决。相反,它在 100
中使用第一个 promise 的 catch
处理程序 (1
) 的 return
值解析(即使它不是显式的——例如:如果我的 catch 会实际上 return 错误。.any()
包装器仍将使用 undefined
解决,而不是等待第一个已履行的承诺。解决,甚至不拒绝!)。
更有趣的是,如果我没有在失败的承诺中指定 catch
处理程序,预期的行为 确实会发生 。看看:
const race = () => Promise.any([
new Promise((resolve, reject) => {
setTimeout(reject, 100, 1);
})
.then(r => (console.log('one resolved with', r), r)),
new Promise((resolve, reject) => {
setTimeout(resolve, 200, 2);
})
.then(r => (console.log('two resolved with', r), r))
])
.then(r => console.log('race resolved with', r))
.catch(err => console.warn('race rejected with', err))
race()
也许我想记录失败!为什么在拒绝时有一些代码 运行 会使父级 Promise.any()
解析为第一个失败承诺的 return 值,该承诺恰好有一个 .catch
处理程序, 而不是等待第一个实现的承诺, as advertised?
无论是否定义 catch
回调,父 Promise.any()
都不应该有相同的行为吗?
这是一个错误吗?我觉得这令人难以置信。
更新:在收到关于为什么会发生这种情况的解释后,我设法获得了我最初寻找的东西(在 catch 块中记录错误,同时仍在等待其他承诺中的第一个实现),通过 returning Promise.reject(err)
在捕获处理程序中:
const race = () => Promise.any([
// first promise, rejects in 100
new Promise((resolve, reject) => {
setTimeout(reject, 100, 1);
})
.then(r => (console.log('one resolved with', r), r))
.catch(err => (console.warn('one rejected with', err), Promise.reject(err))),
// second promise, resolves in 200
new Promise((resolve, reject) => {
setTimeout(resolve, 200, 2);
})
.then(r => (console.log('two resolved with', r), r))
.catch(err => (console.warn('two rejected with', err), Promise.reject(err)))
])
.then(r => console.log('race resolved with', r))
.catch(err => console.warn('race rejected with', err))
race()
当您将 .catch
链接到一个 Promise 上时,整个表达式将计算为已解决的 Promise,该 Promise 的计算结果为 returned 在 .catch
末尾的值。看看下面如何不会导致第二个 .catch
被输入。
Promise.reject()
.catch((err) => { console.log('first catch'); })
.catch((err) => { console.log('second catch'); })
// In other words, the whole below expression resolves to a resolved Promise:
// Promise.reject()
// .catch((err) => { console.log('first catch'); })
整体来说
somePromise
.catch(hander)
为了导致被拒绝的 Promise,handler
必须抛出(或构造和 return 一个被拒绝的 Promise)。
因此,在您的原始代码中:
.catch(err => (console.warn('one rejected with', err), err)),
这使整个表达得到解决。如果你想让它拒绝,把它改成:
.catch(err => {
console.warn('one rejected with', err);
throw err;
}),
(throw
需要独立语句,不能和逗号一起使用)
const race = () => Promise.any([
// first promise, rejects in 100
new Promise((resolve, reject) => {
setTimeout(reject, 100, 1);
})
.then(r => (console.log('one resolved with', r), r))
.catch(err => {
console.warn('one rejected with', err);
throw err;
}),
// second promise, resolves in 200
new Promise((resolve, reject) => {
setTimeout(resolve, 200, 2);
})
.then(r => (console.log('two resolved with', r), r))
.catch(err => (console.warn('two rejected with', err), err))
])
.then(r => console.log('race resolved with', r))
.catch(err => console.warn('race rejected with', err))
race()
解决类似问题的常见方法是 而不是 .catch
除非你可以对错误做一些有用的事情——如果不行,就让被拒绝的 Promise渗透回其调用者,以便其调用者可以处理它。虽然您可以 .catch
并重新抛出,但它有点难看,所以除非必要,否则您可能不想这样做。