如何在没有"快速失败"行为的情况下等待多个并行承诺?

Bra*_*don 45 javascript asynchronous promise async-await

我正在使用async/ 并行await触发多个api调用:

async function foo(arr) {
  const results = await Promise.all(arr.map(v => {
     return doAsyncThing(v)
  }))
  return results
}
Run Code Online (Sandbox Code Playgroud)

我知道的是,不同于loops,Promise.all 执行并行(即,等待换结果部分是并行地).

我也知道:

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

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

还有第三种方式吗?执行是否有效并行,但单一故障不会破坏整个群体?

NoN*_*ded 83

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

你想要做的是伪语言是:

fn task() {
  result-1 = doAsync();
  result-n = doAsync();

  // handle results together
  return handleResults(result-1, ..., result-n)
}
Run Code Online (Sandbox Code Playgroud)

这可以在需要async/ await不需要使用的情况下简单地实现Promise.all.一个工作的例子:

console.clear();

function wait(ms, data) {
  return new Promise( resolve => setTimeout(resolve.bind(this, data), ms) );
}

/** 
 * This will be runned in series, because 
 * we call a function and immediately wait for it's result, 
 * so this will finish in 1s.
 */
async function series() {
  return {
    result1: await wait(500, 'seriesTask1'),
    result2: await wait(500, 'seriesTask2'),
  }
}

/** 
 * While here we call the functions first,
 * then wait for the result later, so 
 * this will finish in 500ms.
 */
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(`Task ${label} starting...`);
  let result = await fn();
  console.log(`Task ${label} finished in ${ Number.parseInt(performance.now() - startTime) } miliseconds with,`, result);
}

void taskRunner(series, 'series');
void taskRunner(parallel, 'parallel');
Run Code Online (Sandbox Code Playgroud)

注意:您需要具有async/ 已await启用以运行此代码段的浏览器.

这样您就可以使用简单try/ catch来处理错误,并在parallel函数内返回部分结果.

  • 怀才不遇.这非常清楚到底发生了什么. (6认同)
  • “ *反模式:解决有错误的诺言不是一个好习惯*”-您从哪里得到的?不,相反,[您的`parallel`函数是反模式”(/sf/ask/3282250331/)可能会导致未处理拒绝。 (3认同)
  • 我投了反对票,因为这个答案没有明确解决错误传播的问题。 (3认同)
  • 更糟糕的是:这甚至不能解决 OP 问题。如果 `task1` 在 `task2` 完成之前被拒绝,你仍然会很快失败。 (2认同)

Ben*_*Ben 39

使用catch意味着promise将解析(除非您从catch或者手动拒绝承诺链中抛出异常),因此您不需要显式返回已解析的promise IIUC.

这意味着只需通过处理错误catch就可以实现您想要的效果.

如果要标准化处理拒绝的方式,则可以对所有承诺应用拒绝处理功能.

async function bar() {
    await new Promise(r=> setTimeout(r, 1000))
      .then(()=> console.log('bar'))
      .then(()=> 'bar result');
}
async function bam() {
    await new Promise((ignore, reject)=> setTimeout(reject, 2000))
      .catch(()=> { console.log('bam errored'); throw 'bam'; });
}
async function bat() {
    await new Promise(r=> setTimeout(r, 3000))
      .then(()=> console.log('bat'))
      .then(()=> 'bat result');
}

function handleRejection(p) {
    return p.catch(err=> ({ error: err }));
}

async function foo(arr) {
  console.log('foo');
  return await Promise.all([bar(), bam(), bat()].map(handleRejection));
}

foo().then(results=> console.log('done', results));
Run Code Online (Sandbox Code Playgroud)

  • 返回等待是多余的fyi https://eslint.org/docs/rules/no-return-await (9认同)
  • 是的,我相信它应该“返回新的Promise”,以便我们可以获得正确的结果数组:https://jsbin.com/ruralujame/edit?html,css,js,console,output (2认同)