这个问题在这里已经有了答案:等待所有承诺完成,即使有些被拒绝(20 个答案)3 年前关闭。
我正在使用 async
/await
并行触发多个 api
调用:
async function foo(arr) {
const results = await Promise.all(arr.map(v => {
return doAsyncThing(v)
}))
return results
}
我知道,与 loops
不同,Promise.all
executes in-parallel(即等待结果部分是并行的)。
如果其中一个元素被拒绝并且 Promise.all 快速失败,则 Promise.all 被拒绝:如果您有四个在超时后解决的 Promise,并且一个立即拒绝,那么 Promise.all 立即拒绝。
当我读到这篇文章时,如果我有 5 个承诺 Promise.all
,而第一个完成的承诺返回一个 reject()
,那么其他 4 个将被有效地取消,并且它们承诺的 resolve()
值将丢失。
有第三种方法吗?哪里的执行实际上是并行的,但一个单一的失败不会破坏整个过程?
catch
),从而避免快速失败。这会做你想要的吗?
const noop = function(){}
Promise.all( arr.map( v => doAsyncThing(v).catch(noop) ) )
将错误转换为未定义值
return doAsyncThing(v)
, return doAsyncThing(v).catch(err => {return err})
吗? ETA:我不确定 catch
正文中应该包含什么——有没有办法在没有 reject
的情况下传递错误对象?只要我可以单独处理 reject()
ed 的 promise 并且仍然从 resolve()
d 的 promise 中获取值。回复:cancelled
与 not propagated
,我不确定我是否理解其中的区别。两者都导致丢失 resolve()
值,是吗?
reject()
的承诺会在结果 array
中作为 undefined
结束吗?
虽然已接受答案中的技术可以解决您的问题,但它是一种反模式。解决带有错误的承诺不是好的做法,并且有一种更简洁的方法来做到这一点。
你想要做的,用伪代码,是:
fn task() {
result-1 = doAsync();
result-n = doAsync();
// handle results together
return handleResults(result-1, ..., result-n)
}
这可以通过 async
/await
轻松实现,无需使用 Promise.all
。一个工作示例:
控制台.clear(); function wait(ms, data) { return new Promise(resolve => setTimeout(resolve.bind(this, data), ms)); } /** * 这些将连续运行,因为我们调用 * 一个函数并立即等待每个结果, * 所以这将在 1 秒内完成。 */ async function series() { return { result1: await wait(500, 'seriesTask1'), result2: await wait(500, 'seriesTask2'), } } /** * 这里我们先调用函数,* 然后稍后等待结果,所以 * 这将在 500 毫秒内完成。 */ async function parallel() { const task1 = wait(500, 'parallelTask1'); const task2 = wait(500, 'parallelTask2'); return { result1: await task1, result2: await task2, } } async function taskRunner(fn, label) { const startTime = performance.now(); console.log(`任务 ${label} 开始...`);让结果 = 等待 fn(); console.log(`Task ${label} 在 ${ Number.parseInt(performance.now() - startTime) } 毫秒内完成,`, result); } void taskRunner(series, 'series');无效的taskRunner(并行,'并行');
注意:您需要一个启用了 async
/await
的浏览器才能运行此代码段。
这样,您可以简单地使用 try
/ catch
来处理您的错误,并在 parallel
函数中返回部分结果。
ES2020 包含 Promise.allSettled,它会做你想做的事。
Promise.allSettled([ Promise.resolve('a'), Promise.reject('b') ]).then(console.log)
输出:
[
{
"status": "fulfilled",
"value": "a"
},
{
"status": "rejected",
"reason": "b"
}
]
但是如果你想“自己动手”,那么你可以利用这样一个事实,即使用 Promise#catch
意味着承诺解决(除非你从 catch
抛出异常或手动拒绝承诺链),所以你不需要显式返回已解决的承诺。
因此,只需使用 catch
处理错误,您就可以实现您想要的。
请注意,如果您希望错误在结果中可见,则必须确定显示它们的约定。
您可以使用 Array#map 对集合中的每个 Promise 应用拒绝处理函数,并使用 Promise.all 等待它们全部完成。
例子
应打印出以下内容:
Elapsed Time Output
0 started...
1s foo completed
1s bar completed
2s bam errored
2s done [
"foo result",
"bar result",
{
"error": "bam"
}
]
async function foo() { await new Promise((r)=>setTimeout(r,1000)) console.log('foo completed') return 'foo result' } async function bar() { await new Promise((r) =>setTimeout(r,1000)) console.log('bar completed') return 'bar result' } async function bam() { try { await new Promise((_,reject)=>setTimeout(reject,2000)) } catch { console.log('bam errored') throw 'bam' } } function handleRejection(p) { return p.catch((error)=>({ error })) } function waitForAll(...ps) { console.log('started...') return Promise.all(ps.map(handleRejection)) } waitForAll(foo(), bar(), bam()).then(results=>console.log('done ', 结果))
请参阅also。
return new Promise
,这样我们才能得到正确的结果数组:jsbin.com/ruralujame/edit?html,css,js,console,output
不定期副业成功案例分享
parallel
function is an antipattern 可能会导致未处理的拒绝。task2
完成之前task1
拒绝,那么您仍然会快速失败。then
中的错误是反模式,因为 Promises 有专门的方法来处理catch
分支中的错误,因此开发人员期望then
中的值和catch
中的错误。将其混合起来会导致其他不了解您的自定义实现细节的人感到困惑。