.then(成功、失敗)はいつ約束のアンチパターンと見なされますか?


188

ブルーバードプロミスFAQを見てみましたが、これ.then(success, fail)はアンチパターンであると述べています。トライアンドキャッチについては説明がよくわかりません。これの次の何が問題になっていますか?

some_promise_call()
.then(function(res) { logger.log(res) }, function(err) { logger.log(err) })

例は正しい方法として次のことを示唆しているようです。

some_promise_call()
.then(function(res) { logger.log(res) })
.catch(function(err) { logger.log(err) })

違いは何ですか?


1
then().catch()カンマを探してこのコールバックが成功または失敗の分岐かどうかを調査する必要がないため、より読みやすくなっています。
Krzysztof Safjanowski 14

7
@KevinB:大きな違いがあります。答えを確認してください
Bergi

12
@KrzysztofSafjanowski-「見た目が良い」という議論に打ちのめされました。まったく間違っている!
Andrey Popov

6
注:を使用している場合.catch、どのステップが問題の原因であるかはわかりません-最後thenまたは他のどこかでプロミスチェーンの内側にあります。したがって、独自の欠点があります。
vitaly-t 2015

2
私はいつも)(約束の.thenに関数名を追加するためにparamsはそれ読めるすなわちsome_promise_call() .then(function fulfilled(res) { logger.log(res) }, function rejected(err) { logger.log(err) })
シェーンRowatt

回答:


215

違いは何ですか?

.then()コールは、コールバックがエラーをスローする場合には拒否されます約束を返します。つまり、成功loggerが失敗すると、エラーは次の.catch()コールバックに渡されますが、failと一緒に行くコールバックには渡されませんsuccess

これは制御フロー図です:

2つの引数を持つthenの制御フロー図 then catchチェーンの制御フロー図

同期コードでそれを表現するには:

// some_promise_call().then(logger.log, logger.log)
then: {
    try {
        var results = some_call();
    } catch(e) {
        logger.log(e);
        break then;
    } // else
        logger.log(results);
}

2番目log(の最初の引数と同様.then())は、例外が発生しなかった場合にのみ実行されます。ラベル付けされたブロックとbreakステートメントは少し奇妙に感じます、これは実際にはpythonが持っているものtry-except-elseです(推奨読書!)。

// some_promise_call().then(logger.log).catch(logger.log)
try {
    var results = some_call();
    logger.log(results);
} catch(e) {
    logger.log(e);
}

catchロガーはまた、成功のロガーの呼び出しからの例外を処理します。

違いはこれくらいです。

トライアンドキャッチについては説明がよくわかりません

議論は、通常、処理のすべてのステップでエラーをキャッチする必要があり、チェーンでそれを使用すべきではないということです。すべてのエラーを処理する最終ハンドラーは1つしかないことが予想されますが、「アンチパターン」を使用すると、then-callbackの一部のエラーが処理されません。

ただし、このパターンは実際には非常に便利です。このステップで発生したエラーを処理し、エラーが発生しなかったとき、つまりエラーが回復不能な場合に、まったく異なる処理を実行したい場合。これにより、制御フローが分岐することに注意してください。もちろん、これが望ましい場合もあります。


これの次の何が問題になっていますか?

some_promise_call()
.then(function(res) { logger.log(res) }, function(err) { logger.log(err) })

コールバックを繰り返す必要があったこと。あなたはむしろ

some_promise_call()
   .catch(function(e) {
       return e; // it's OK, we'll just log it
   })
   .done(function(res) {
       logger.log(res);
   });

.finally()このために使用することも検討してください。


7
これは、私が数日読んだ中で最も役立つ説明です(そして私はたくさん読んだ)。私がどれほど感謝しているのか説明できません!:) 2つの違いをもっと強調する必要があると思います。これにより、成功関数の内部でもエラー.catchキャッチできます。複数のアクションが、これは私の問題です。とにかく-情報をありがとう!他にいくつか質問できるように、共有したいオンラインコミュニケーションツールはありませんか?:P
アンドレイポポフ

2
これにより、ここでさらに賛成票が得られることを願っています。間違いなくPromise、このサイトの重要なメカニズムの最良の説明の1つです。
Patrick Roberts

2
.done()標準の一部ではありませんか?少なくともMDNはそのメソッドをリストしていません。参考になります。
ygoe 2019年

1
@ygoe確かに。+未処理の拒否の検出doneによって基本的に廃止されたBluebirdのことですthen
ベルギ

1
色覚異常からの単なるメモ:図は意味をなさない:)
Benny K

37

2つはまったく同じではありません。違いは、最初の例では、successハンドラーでスローされた例外をキャッチしないことです。そのため、よくあることですが、メソッドが解決されたpromiseのみを返す場合は、トレーリングcatchハンドラー(またはthen空のsuccessパラメーターを持つ別のハンドラー)が必要です。確かに、thenハンドラーが潜在的に失敗する可能性のあることを何も実行していない可能性thenがあります。その場合、2つのパラメーターを1つ使用するだけで問題ありません。

しかし、あなたがリンクしたテキストのポイントは、then一連の非同期ステップをチェーンする機能において、コールバックと比較してほとんど有用であると私は信じています。実際にこれを行うと、2パラメータ形式のthen微妙な動作は期待どおりに動作しません、上記の理由により。ミッドチェーンで使用すると、直感に反します。

多くの複雑な非同期処理を実行し、このようなコーナーにぶつかった人は、認めざるをえないほど、このアンチパターンを避けて、別のハンドラーアプローチを使用することをお勧めします。


18

両方の長所と短所を見ることで、状況にどちらが適切であるかを計算で推測できます。これらは、promiseを実装するための2つの主要なアプローチです。どちらにもプラスとマイナスがあります

キャッチアプローチ

some_promise_call()
.then(function(res) { logger.log(res) })
.catch(function(err) { logger.log(err) })

メリット

  1. すべてのエラーは1つのcatchブロックによって処理されます。
  2. thenブロックで例外もキャッチします。
  3. 複数の成功コールバックの連鎖

短所

  1. 連鎖している場合、異なるエラーメッセージを表示することが難しくなります。

成功/エラーアプローチ

some_promise_call()
.then(function success(res) { logger.log(res) },
      function error(err) { logger.log(err) })

メリット

  1. きめ細かいエラー制御ができます。
  2. dbエラー、500エラーなど、さまざまなカテゴリのエラーに共通のエラー処理機能を使用できます。

短所

  1. catch成功コールバックによってスローされたエラーを処理したい場合は、別のものが必要です。

ログファイルだけを使用して本番環境の問題をデバッグする必要がある人にとっては、成功/エラーアプローチをお勧めします。これにより、アプリの出口境界で記録できる原因となるエラーチェーンを作成できるようになります。
Shane Rowatt 16

質問。いくつかのことの1つを実行する非同期呼び出しを行うとしましょう:1)正常に戻ります(2xxステータスコード)、2)失敗して戻りますが(4xxまたは5xxコード)、それ自体は拒否されません、3)またはまったく戻りません(インターネット接続がダウンしています)。ケース#1の場合、.thenの成功コールバックがヒットします。ケース#2の場合、.thenのエラーコールバックがヒットします。ケース#3の場合、.catchが呼び出されます。これは正しい分析ですよね?ケース#2は技術的に最もトリッキーです。技術的には4xxまたは5xxは拒否ではなく、正常に戻ります。したがって、.then内で処理する必要があります。....私の理解は正しいですか?
ベンジャミンホフマン

「ケース#2の場合、.thenのエラーコールバックがヒットします。ケース#3の場合、.catchが呼び出されます。これは正しい分析ですよね?」-これがフェッチの仕組みです
aWebDeveloper '

2

簡単な説明:

ES2018で

引数onRejectedを指定してcatchメソッドを呼び出すと、次の手順が実行されます。

  1. この値を約束にします。
  2. 戻る?Invoke(promise、 "then"、«undefined、onRejected»)。

つまり:

promise.then(f1).catch(f2)

等しい

promise.then(f1).then(undefiend, f2)

1

を使用.then().catch()すると、ワークフローの実行に必要なプロミスチェーンを有効にできます。データベースから情報を読み取る必要がある場合は、その情報を非同期APIに渡し、応答を操作する必要があります。応答をデータベースにプッシュすることができます。これらすべてのワークフローをコンセプトで処理することは可能ですが、管理が非常に困難です。より良い解決策はthen().then().then().then().catch()、一度のキャッチですべてのエラーを受け取り、コードの保守性を維持できるようにすることです。


0

を使用して、promiseで成功および失敗のチェーンハンドラーthen()catch()支援します。catch()によって返される約束に取り組みthen()ます。扱います

  1. 約束が拒否された場合。写真の#3を参照
  2. then()の成功ハンドラでエラーが発生した場合、以下の4〜7行目の間。写真の#2.aを参照してください(障害コールバックthen()はこれを処理しません。)
  3. then()の失敗ハンドラでエラーが発生した場合、以下の行番号8。写真の#3.bを参照してください。

1. let promiseRef: Promise = this. aTimetakingTask (false); 2. promiseRef 3. .then( 4. (result) => { 5. /* successfully, resolved promise. 6. Work on data here */ 7. }, 8. (error) => console.log(error) 9. ) 10. .catch( (e) => { 11. /* successfully, resolved promise. 12. Work on data here */ 13. });

ここに画像の説明を入力してください

注意:多くの場合、catch()すでにが記述されている場合、失敗ハンドラは定義されない可能性があります。編集:エラーハンドラーが定義されていない場合にのみreject()呼び出されます。画像の#3に注目してください。行番号8および9のハンドラーが定義されていない場合に呼び出されます。catch()then()catch()

then()コールバックがそれを処理している場合、によって返されるpromiseにはエラーがないので、それは理にかなっています。


番号3からcatchコールバックへの矢印が間違っているようです。
ベルギ

ありがとう!then()でエラーコールバックが定義されていると、呼び出されません(コードスニペットの8行目と9行目)。#3は2つの矢印の1つを呼び出します。コールバックが処理する場合、then()によって返されるpromiseにエラーがないため、これは理にかなっています。回答を編集しました!
VenCKi

-1

言葉ではなく、良い例。次のコード(最初の約束が解決された場合):

Promise.resolve()
.then
(
  () => { throw new Error('Error occurs'); },
  err => console.log('This error is caught:', err)
);

と同じです:

Promise.resolve()
.catch
(
  err => console.log('This error is caught:', err)
)
.then
(
  () => { throw new Error('Error occurs'); }
)

しかし、最初の約束が拒否されたため、これは同じではありません。

Promise.reject()
.then
(
  () => { throw new Error('Error occurs'); },
  err => console.log('This error is caught:', err)
);

Promise.reject()
.catch
(
  err => console.log('This error is caught:', err)
)
.then
(
  () => { throw new Error('Error occurs'); }
)

4
これは意味がありません。この回答を削除していただけませんか?それは誤解を招きやすく、正しい答えを混乱させます。
アンディレイ

@AndyRay、これは実際のアプリケーションでは意味がありませんが、約束の働きを理解することは意味があります。
ktretyak 2018年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.