1. ホーム
  2. javascript

[解決済み] 複数のプロミスを並列に待ち、フェイルファストさせない方法は?重複

2022-09-16 14:23:02

質問

を使っています。 async / await を複数回発射する api を並列に呼び出すことができます。

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

とは違って loops , Promise.all は並列に実行されます。 (つまり、結果待ちの部分は並列に実行されます)。

しかし も知っています。 :

Promise.allは、要素の1つが拒否された場合、そして Promise.allは高速に失敗します。タイムアウト後に解決する4つの約束があったとして タイムアウト後に解決する4つの約束があり、1つがすぐに拒否された場合、Promise.allはすぐに拒否されます。 を即座に拒否します。

これを読むと、もし私が Promise.all で5つのプロミスを実行し、最初に終了したプロミスが reject() を返すと、他の 4 つは事実上キャンセルされ、その約束された resolve() の値は失われます。

3つ目の方法はありますか?実行は事実上並列ですが、1つの失敗で全体が台無しにならないような方法はありますか?

どのように解決するのですか?

ES2020の内容 プロミス.allSettled を含んでおり、これはあなたが望むことを行います。

Promise.allSettled([
    Promise.resolve('a'),
    Promise.reject('b')
]).then(console.log)

出力します。

[
  {
    "status": "fulfilled",
    "value": "a"
  },
  {
    "status": "rejected",
    "reason": "b"
  }
]

しかし、もしあなたが自分自身でロール・アップしたいのであれば、quot;roll your own" を使って Promise#catch を使うということは、プロミスが解決されるということです(あなたが catch から例外を投げるか,手動でプロミスの連鎖を拒否しない限り),解決されたプロミスを明示的に返す必要はないことを意味します.

というわけで、単純にエラー処理を catch で処理するだけで、やりたいことが実現できるのです。

もしエラーが結果で見えるようにしたいのであれば、それらを表面化するための規約を決めなければならないことに注意してください。

コレクション内の各プロミスに拒否処理関数を適用するには、以下のようにします。 Array#map を使用し、さらに プロミス.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', results))

参照 また .