ChatGPT解决这个技术问题 Extra ChatGPT

如何在没有“快速失败”行为的情况下并行等待多个承诺? [复制]

这个问题在这里已经有了答案:等待所有承诺完成,即使有些被拒绝(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(即等待结果部分是并行的)。

但是I also know that

如果其中一个元素被拒绝并且 Promise.all 快速失败,则 Promise.all 被拒绝:如果您有四个在超时后解决的 Promise,并且一个立即拒绝,那么 Promise.all 立即拒绝。

当我读到这篇文章时,如果我有 5 个承诺 Promise.all,而第一个完成的承诺返回一个 reject(),那么其他 4 个将被有效地取消,并且它们承诺的 resolve() 值将丢失。

有第三种方法吗?哪里的执行实际上是并行的,但一个单一的失败不会破坏整个过程?

其他四个没有被取消,但它们的结果不会沿着承诺链(IIUC)传播。
如果您想避免拒绝承诺链的特定失败模式,那么您可以在子承诺链中处理该失败(使用 catch),从而避免快速失败。这会做你想要的吗?
const noop = function(){} Promise.all( arr.map( v => doAsyncThing(v).catch(noop) ) ) 将错误转换为未定义值
@BenAston 你的意思是代替 return doAsyncThing(v), return doAsyncThing(v).catch(err => {return err}) 吗? ETA:我不确定 catch 正文中应该包含什么——有没有办法在没有 reject 的情况下传递错误对象?只要我可以单独处理 reject() ed 的 promise 并且仍然从 resolve() d 的 promise 中获取值。回复:cancellednot propagated,我不确定我是否理解其中的区别。两者都导致丢失 resolve() 值,是吗?
@Thomas reject() 的承诺会在结果 array 中作为 undefined 结束吗?

j
jarmod

虽然已接受答案中的技术可以解决您的问题,但它是一种反模式。解决带有错误的承诺不是好的做法,并且有一种更简洁的方法来做到这一点。

你想要做的,用伪代码,是:

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 函数中返回部分结果。


被低估了。这很清楚到底发生了什么。
反模式:用错误解决承诺不是好的做法” - 你从哪里得到的?不,相反,your parallel function is an antipattern 可能会导致未处理的拒绝。
更糟糕的是:这甚至不能解决 OPs 问题。如果在 task2 完成之前 task1 拒绝,那么您仍然会快速失败。
你从哪里得到的? - 处理 then 中的错误是反模式,因为 Promises 有专门的方法来处理 catch 分支中的错误,因此开发人员期望 then 中的值和catch 中的错误。将其混合起来会导致其他不了解您的自定义实现细节的人感到困惑。
我投了反对票,因为这个答案没有明确解决错误传播的方法。
B
Ben Aston

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 await 是多余的 fyi eslint.org/docs/rules/no-return-await
为什么在已经使用 await 的情况下还要使用 .then?您可以删除所有 .then 调用,它会起作用。
你的脚本的输出不包括'bar result'和'bat result',但是:done [ undefined, { "error": "bam" }, undefined ] 是故意的吗?
是的,我相信它应该是 return new Promise,这样我们才能得到正确的结果数组:jsbin.com/ruralujame/edit?html,css,js,console,output
添加到 David 的评论中,返回了 done [ undefined, { "error": "bam" }, undefined ]。如果添加 return await 和 then for bam,则显示结果 [Log] done – ["bar result", "bam result", "bat result"]。但是我无法完成结果 - ["bar result", {"error":"bam"}, "bat result"]。有没有办法做到这一点?