首页 文章

如何在没有'fail-fast'行为的情况下等待多个Promise并行? [重复]

提问于
浏览
26

这个问题在这里已有答案:

我正在使用 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立即拒绝 .

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

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

2 回答

  • 53

    使用 catch 表示promise解析(除非您从 catch 抛出异常或手动拒绝promise链),因此您不需要显式返回已解析的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));
    
  • 25

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

    你想要做的是伪语言是:

    fn task() {
      result-1 = doAsync();
      result-n = doAsync();
    
      // handle results together
      return handleResults(result-1, ..., result-n)
    }
    

    这可以通过 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');
    

    注意:您需要一个启用了 async / await 的浏览器才能运行此代码段 .

    这样您只需使用 try / catch 来处理错误,并在 parallel 函数内返回部分结果 .

相关问题