🫠

Promise.allで失敗したものだけ検知したい

2022/08/08に公開1

結論

Promise.allSettled()を使いましょう

Promise.allSettled()とは

複数のPromiseを同時に実行しそれぞれについて成功、失敗と結果、エラー内容を返してくれます。
どれかが失敗しても全てのPromiseが完了するまで待機します。

TypeScriptの場合、返ってくる型は下記のようになります(抜粋)

{
    status: "fulfilled"
    value: T
} | 
{
    status: "rejected"
    reason: any
}[]

各結果に対して、status === 'fulfilled'ならば実行結果がvalueとして、status === 'rejected'ならば失敗理由がreasonとして取得できます。

Promise.allとの違い

Promise.allの場合はPromiseのうちどれか一つでも失敗するとPromise.all自体が失敗したような感じになりcatch節に入ります。
失敗するPromiseを含んで実行した場合の結果の違いは下記のようになります。

// 失敗
const promise1 = new Promise((resolve, reject) => {
    reject()
})
// 成功
const promise2 = new Promise((resolve, reject) => {
    resolve('resolve')
})

// => error
Promise.all([promise1, promise2]).then(() => {
    console.log("done")
}).catch(() => {
    console.log("error")
})
// => done
Promise.allSettled([promise1, promise2]).then(() => {
    console.log("done")
}).catch(() => {
    console.log("error")
})

使い分け

MDNにあるように、各Promiseに依存があるかないかなどで判断します。

お互いに依存せずに正常に完了する場合や、各プロミスの結果を常に知りたい場合

  • Promise.allSettled

タスクがお互いに依存している場合や、タスクのいずれかが拒否されたときにすぐに拒否したい場合

  • Promise.all

MDN

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled

Discussion

nap5nap5

neverthrowライブラリだとエラーのみを抽出してくれる Result.combineWithAllErrorsがあります。
これで少しデモも作ってみました。

https://codesandbox.io/p/sandbox/admiring-herschel-px2sum?file=%2Fsrc%2Fvanilla.test.ts

また、バニラだと型のnarrowingでやや手数が多いのですが、そのときのワークアラウンドも作ってみました。
https://github.com/microsoft/TypeScript/issues/42012#issuecomment-952239238

簡単ですが、以上です。