漁獲の前と後の配置


103

.catch入れ子になった約束でBEFOREとAFTERの違いを理解するのに苦労します。

代替1:

test1Async(10).then((res) => {
  return test2Async(22)
    .then((res) => {
      return test3Async(100);
    }).catch((err) => {
      throw "ERROR AFTER THEN";
    });
}).then((res) => {
  console.log(res);
}).catch((err) => {
  console.log(err);
});

代替2:

test1Async(10).then((res) => {
   return test2Async(22)
     .catch((err) => {
        throw "ERROR BEFORE THEN";
      })
      .then((res) => {
        return test3Async(100);
      });
  }).then((res) => {
    console.log(res);
  }).catch((err) => {
    console.log(err);
  });

各関数の動作は次の<0とおりです。number がtest2の場合、test1は失敗し、numberがtest2の場合> 10、test3は失敗します100。この場合、test2は失敗しています。

test2Asyncを実行して失敗させたところ、BEFOREとAFTERの両方が同じように動作し、test3Asyncが実行されません。誰かがキャッチを別の場所に置くことの主な違いを私に説明できますか?

各関数でconsole.log('Running test X')、それが実行されるかどうかを確認するために。

この質問は、私が投稿した以前のスレッドが原因で発生します。。別の問題であり、別のトピックを投稿する価値があると思います。


.thenと.catchはどちらも約束を変更する可能性があります...ですから、誤解がどこから来たかはわかりません。.thenの前にcatchを置くと、.thenと.thenの前に発生した拒否をキャッチし、.catch内で発生したことに基づいて、完了/失敗コールバックを実行します。
ケビンB

私の質問が明確でない場合は申し訳ありません。しかし、先ほど述べたように、このケースでは両方のケースが同じように動作するため、違いはわかりません。いつBEFOREをBEFOREに配置し、いつそれをAFTERに配置することにしたか教えていただけますか?それを後に置くことは本当に直感的で一般的です。なぜそれを前に置くのかわからない
Zanko

それらが同じように機能するのは、それぞれがこの特定のケースで結果を変更しないためです。どちらかを少し変更すると、結果が変わる可能性があります。
Kevin B

「結果を変える」とはどういう意味ですか。申し訳ありませんが、私は本当に混乱している
Zanko

たとえば、エラーをスローする代わりに何もしなかった場合、プロミスは拒否から解決に切り替わります。もちろん、約束は拒否された約束ではなく解決された約束になっているため、結果は当然変わります。(もちろん、それがすでに解決されている場合を除き、その場合、漁獲量はとにかく実行されませんでした)
Kevin B

回答:


237

したがって、基本的には、これら2つの違いはどこにあるのかを尋ねています(p以前のコードから作成されたpromiseはどこにありますか)。

return p.then(...).catch(...);

そして

return p.catch(...).then(...);

pが解決または拒否するときに違いがありますが、それらの違いが重要であるかどうかは、.then()または.catch()ハンドラー内のコードが何をするかに依存します。

p解決するとどうなるか:

最初のスキームでは、p解決時に.then()ハンドラーが呼び出されます。その.then()ハンドラーが値または最終的に解決する別のpromiseを返す場合、.catch()ハンドラーはスキップされます。ただし、.then()ハンドラーが最終的に拒否するpromiseをスローまたは返す場合、ハンドラー.catch()は元のpromiseのreject pだけでなく、.then()ハンドラーで発生したエラーの両方に対して実行されます。

2番目のスキームでは、p解決時に.then()ハンドラーが呼び出されます。その.then()ハンドラーがスローするか、最終的に拒否するプロミスを返す場合、ハンドラー.catch()はチェーンの前にあるため、それをキャッチできません。

それが違い#1です。.catch()ハンドラーがAFTERの場合、ハンドラー内のエラーもキャッチでき.then()ます。

p拒否した場合:

これで、最初のスキームでは、promiseがp拒否された場合、.then()ハンドラーはスキップされ、.catch()期待どおりにハンドラーが呼び出されます。.catch()ハンドラーで何をするかによって、最終結果として何が返されるかが決まります。.catch()ハンドラーから値を返すか、最終的に解決するpromiseを返す場合、エラーを「処理」して正常に返されたため、promiseチェーンは解決済み状態に切り替わります。.catch()ハンドラーで拒否されたプロミスをスローまたは返す場合、返されたプロミスは拒否されたままになります。

2番目のスキームでは、promiseがp拒否された場合、.catch()ハンドラーが呼び出されます。通常の値または最終的に.catch()ハンドラーから解決されるプロミス(つまり、エラーの「処理」)を返す場合、プロミスチェーンは解決済み状態に切り替わり.then().catch()が呼び出された後にハンドラーがハンドラーに切り替わります。

それが違い#2です。.catch()ハンドラーがBEFOREである場合、エラーを.then()処理し、ハンドラーを引き続き呼び出すことができます。

いつ使用するか:

.catch()元のpromise pまたは.then()ハンドラーのいずれかでエラーをキャッチできるハンドラーが1つだけp必要で、reject from が.then()ハンドラーをスキップする場合は、最初のスキームを使用します。

元のプロミスのエラーをキャッチしたい場合は、2番目のスキームを使用します。また、p状況に応じて、プロミスチェーンを解決された状態で続行できるようにして、.then()ハンドラーを実行します。

他のオプション

次の.then()ように渡すことができる両方のコールバックを使用するオプションが1つあります。

 p.then(fn1, fn2)

唯一の1この保証fn1またはfn2これまでに呼び出されます。場合はp解決した後fn1に呼び出されます。p拒否した場合は、fn2呼び出されます。の結果に変化が生じてfn1fn2、呼び出されたり、その逆は起こりえません。そのため、ハンドラー自体で何が発生したかに関係なく、2つのハンドラーのうち1つだけが呼び出されるようにしたい場合は、を使用できますp.then(fn1, fn2)


17
質問は、具体的に.then()との順序について.catch()です。さらに、どの順序をいつ使用するかに関するいくつかのヒントを示します。ここでは、3番目のオプション、つまり成功ハンドラとエラーハンドラの両方を.then()に渡すことをお勧めします。その場合、多くても1つのハンドラーが呼び出されます。
ArneHugo 2017年

7
@ArneHugo-良い提案。追加した。
jfriend00

では、Promise Chainingの実行中に.then .catch .catch .thenのようなシナリオを記述できますか?
Kapil Raghuwanshi

@KapilRaghuwanshi、はい、失敗した場合にデフォルト値を渡すために使用できます。ie Promise.reject(new Error("F")).then(x => x).catch(e => {console.log(e); return [1]}).then(console.log)および Promise.resolve([2]).then(x => x).catch(e => [1]).then(console.log)
CervEd

1
@DmitryShvedov-私が推測したように、これは間違ってい.then(this.setState({isModalOpen: false}))ます。.then()括弧内のコードが(promiseが解決される前に)すぐに実行されるように、関数参照を渡していない。それはあるはずです.then(() => this.setState({isModalOpen: false}))
jfriend00

31

jfriend00の答えはすばらしいですが、類似の同期コードを追加することをお勧めします。

return p.then(...).catch(...);

同期に似ています:

try {
  iMightThrow() // like `p`
  then()
} catch (err) {
  handleCatch()
}

iMightThrow()スローしない場合はthen()呼び出されます。スローする場合(またはスローする場合then())、handleCatch()呼び出されます。catchブロックthenが呼び出されるかどうかを制御できないことに注意してください。

一方、

return p.catch(...).then(...);

同期に似ています:

try {
  iMightThrow()
} catch (err) {
  handleCatch()
}

then()

この場合、iMightThrow()スローしない場合はthen()実行されます。スローhandleCatch()する場合then()は、呼び出されるかどうかを決定する必要がありhandleCatch()ます。再スローthen()される場合、例外は呼び出し元にすぐにスローされるため、呼び出されません。handleCatch()問題を適切に処理できる場合は、then()呼び出されます。


これは良い説明ですが、あなたは孤児を包むことができthen()finally{...}
tyskr

2
@ 82Tuskers、よろしいですか?私が入れthen()finally{...}場合、handleCatch()投げても間違って呼び出されませんか?私の目標は、例外を処理するさまざまな方法を提案するのではなく、類似の同期コードを示すことでした
akivajgordon

したがって、すべてのケースを処理したいが.then()をチェーンしたい場合は、.then(do something).catch(log err and update state).then(do anotherthing).catch(log err)を使用するのが最善です。すべてのポイントでキャッチしようとしますが、stmntsを実行し続けますか?
アンナ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.