[解決済み】一部の約束が拒否されても、すべての約束が完了するまで待つ
質問
のセットがあるとします。
Promise
がネットワークリクエストをしていて、そのうちの1つが失敗する。
// http://does-not-exist will throw a TypeError
var arr = [ fetch('index.html'), fetch('http://does-not-exist') ]
Promise.all(arr)
.then(res => console.log('success', res))
.catch(err => console.log('error', err)) // This is executed
例えば、1つが失敗しても、これらすべてが終了するまで待ちたいとします。ネットワークエラーが発生し、そのリソースがなくても生きていけるが、もし手に入るなら先に手に入れたいリソースがあるかもしれない。私はネットワーク障害を優雅に扱いたいのです。
以来
Promise.all
はこのための場所を残しません、約束ライブラリを使用せずに、これを処理するための推奨されるパターンは何ですか?
どのように解決するのですか?
Benjaminの回答は、この問題を解決するための素晴らしい抽象化を提供していますが、私はより抽象化されていない解決策を望んでいました。この問題を解決するための明示的な方法は、単に
.catch
を内部プロミス上で実行し、そのコールバックからエラーを返します。
let a = new Promise((res, rej) => res('Resolved!')),
b = new Promise((res, rej) => rej('Rejected!')),
c = a.catch(e => { console.log('"a" failed.'); return e; }),
d = b.catch(e => { console.log('"b" failed.'); return e; });
Promise.all([c, d])
.then(result => console.log('Then', result)) // Then ["Resolved!", "Rejected!"]
.catch(err => console.log('Catch', err));
Promise.all([a.catch(e => e), b.catch(e => e)])
.then(result => console.log('Then', result)) // Then ["Resolved!", "Rejected!"]
.catch(err => console.log('Catch', err));
さらに一歩進んで、次のような一般的なキャッチ・ハンドラを書くこともできます。
const catchHandler = error => ({ payload: error, resolved: false });
を実行すると
> Promise.all([a, b].map(promise => promise.catch(catchHandler))
.then(results => console.log(results))
.catch(() => console.log('Promise.all failed'))
< [ 'Resolved!', { payload: Promise, resolved: false } ]
この問題は、キャッチされた値がキャッチされていない値と異なるインターフェイスを持つことです。
const successHandler = result => ({ payload: result, resolved: true });
これで、こんなことができるようになったわけです。
> Promise.all([a, b].map(result => result.then(successHandler).catch(catchHandler))
.then(results => console.log(results.filter(result => result.resolved))
.catch(() => console.log('Promise.all failed'))
< [ 'Resolved!' ]
そして、DRYを保つために、Benjaminの答えにたどり着きます。
const reflect = promise => promise
.then(successHandler)
.catch(catchHander)
のようになります。
> Promise.all([a, b].map(result => result.then(successHandler).catch(catchHandler))
.then(results => console.log(results.filter(result => result.resolved))
.catch(() => console.log('Promise.all failed'))
< [ 'Resolved!' ]
2番目のソリューションの利点は、抽象化され、DRYであることです。デメリットはコードが増えることと、一貫性を持たせるためにすべてのプロミスを反映させることを忘れてはいけないことです。
私の解決策は、明示的でKISSと言えますが、確かに堅牢性は低いです。このインターフェースは、プロミスが成功したか失敗したかを正確に知ることを保証するものではありません。
例えばこんなのがありますよね。
const a = Promise.resolve(new Error('Not beaking, just bad'));
const b = Promise.reject(new Error('This actually didnt work'));
に引っかからない。
a.catch
ということで
> Promise.all([a, b].map(promise => promise.catch(e => e))
.then(results => console.log(results))
< [ Error, Error ]
どれが致命的でどれが致命的でないかを見分ける方法はないのです。もしそれが重要なら、成功したかどうかを追跡するインターフェイスを強制することになるでしょう(これは
reflect
が行います)。
エラーを潔く処理したいだけなら、エラーを未定義値として扱えばいいのです。
> Promise.all([a.catch(() => undefined), b.catch(() => undefined)])
.then((results) => console.log('Known values: ', results.filter(x => typeof x !== 'undefined')))
< [ 'Resolved!' ]
私の場合、エラーの内容や失敗の原因を知る必要はなく、値があるかどうかだけが気になります。具体的なエラーのログは、プロミスを生成する関数に任せます。
const apiMethod = () => fetch()
.catch(error => {
console.log(error.message);
throw error;
});
そうすれば、アプリケーションの他の部分はそのエラーを無視してもいいし、未定義の値として扱ってもいいのです。
私は高レベルの関数が安全に失敗し、依存関係が失敗した理由の詳細を気にしないようにしたいのですが、そのトレードオフをしなければならないときはDRYよりもKISSを好みます。
reflect
.
関連
-
[解決済み】Angular JS Uncaught Error。[インジェクター:モジュラー]。
-
[解決済み】Javascriptのコールバック関数がFirefoxで「Callback is not a function」というエラーを投げる
-
[解決済み】SyntaxError: 'import' と 'export' は 'sourceType: module' とだけ表示されるかもしれない - Gulp
-
[解決済み] テスト
-
[解決済み】エラー:リクエストのエンティティが大きすぎる
-
[解決済み】npm install --legacy-peer-deps は具体的に何をするのですか?どんなときに推奨されるのか/どんな使用例が考えられるのか?
-
[解決済み】 \u003C とは何ですか?
-
[解決済み] ある要素が存在するまで待つには?
-
[解決済み] jQuery の Ajax リクエストがすべて終了するまで待ちますか?
-
[解決済み】要素が存在するまで関数を待機させる
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】JavaScriptで':'(コロン)は何をするのか?
-
[解決済み] jQueryで、ユーザーがそのフィールドを編集している間、テキストフィールドの最初の文字を大文字にするにはどうすればよいですか?
-
[解決済み】コンソールがUnterminated JSX contentsエラーを投げる【終了しました
-
[解決済み] [Solved] Uncaught TypeError: nullのプロパティ 'appendChild' を読み取ることができない。
-
[解決済み】Javascript - ERR_CONTENT_LENGTH_MISMATCH
-
[解決済み】エラー:リスン EACCES 0.0.0.0:80 OSx Node.js
-
[解決済み】Uncaught SyntaxError: JSON の位置 0 に予期しないトークン u があります。
-
[解決済み】TypeError: res.status は関数ではありません。
-
[解決済み】Syntax error: JavaScriptの不正なreturnステートメント
-
[解決済み】module.exports "モジュールが定義されていません"