Platzierung des Fangs VOR und NACH diesem Zeitpunkt

102

Ich habe Probleme, den Unterschied zwischen Putten zu verstehen .catch VOR und NACH einem verschachtelten Versprechen .

Alternative 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);
});

Alternative 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);
  });

Das Verhalten jeder Funktion ist wie folgt: Test1 <0schlägt fehl, wenn Nummer ist. > 10Test2 schlägt fehl, wenn Nummer ist, und Test3 schlägt fehl, wenn Nummer nicht ist 100. In diesem Fall schlägt test2 nur fehl.

Ich habe versucht, test2Async auszuführen und zum Fehlschlagen zu bringen. BEFORE und AFTER verhalten sich dann gleich und führen test3Async nicht aus. Kann mir jemand den Hauptunterschied für das Platzieren von Fängen an verschiedenen Orten erklären?

In jeder Funktion I, console.log('Running test X')um zu überprüfen, ob es ausgeführt wird.

Diese Frage stellt sich aufgrund des vorherigen Threads, den ich gepostet habe. Wie kann man einen verschachtelten Rückruf in ein Versprechen verwandeln? . Ich denke, es ist ein anderes Problem und es lohnt sich, ein anderes Thema zu veröffentlichen.

Zanko
quelle
Sowohl .then als auch .catch können das Versprechen ändern ... also bin ich mir nicht sicher, woher das Missverständnis kommt. Wenn Sie catch vor dem .then setzen, werden Ablehnungen abgefangen, die vor dem .then und dem .then aufgetreten sind. Die Rückrufe werden ausgeführt / fehlgeschlagen, basierend auf dem, was im .catch passiert, und umgekehrt, wenn Sie sie austauschen.
Kevin B
Entschuldigung, wenn meine Frage nicht klar war. Aber in diesem Fall verhalten sich beide Fälle, wie gesagt, gleich, sodass ich den Unterschied nicht erkennen kann. Können Sie mir sagen, wann wir den Fang VORHER setzen und wann wir uns entschlossen haben, ihn NACHHER zu setzen? es nach zu setzen scheint wirklich intuitiv und üblich. Nur nicht sicher, warum wir es manchmal vorher setzen
Zanko
Wenn sie die gleiche Leistung erbringen, liegt dies einfach daran, dass das, was jeder tut, das Ergebnis in diesem speziellen Fall nicht verändert. Eine geringfügige Änderung an beiden könnte das Ergebnis verändern.
Kevin B
Was meinst du mit "das Ergebnis verändern"? Entschuldigung, ich bin wirklich verwirrt, haha
Zanko
Wenn Sie beispielsweise anstelle eines Fehlers nichts unternommen haben, wird das Versprechen von abgelehnt zu gelöst geändert. Das würde natürlich das Ergebnis verändern, denn das Versprechen ist jetzt eher ein gelöstes Versprechen als ein abgelehntes. (es sei denn natürlich, es wurde bereits gelöst, in diesem Fall wäre der Fang sowieso nicht gelaufen)
Kevin B

Antworten:

236

Im Grunde fragen Sie sich also, was der Unterschied zwischen diesen beiden ist (wo pist ein Versprechen, das aus einem vorherigen Code erstellt wurde):

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

und

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

Es gibt Unterschiede, entweder wenn p aufgelöst oder abgelehnt wird, aber ob diese Unterschiede wichtig sind oder nicht, hängt davon ab, was der Code in den .then()oder .catch()Handlern tut.

Was passiert, wenn pgelöst wird:

Im ersten Schema wird beim pAuflösen der .then()Handler aufgerufen. Wenn dieser .then()Handler entweder einen Wert oder ein anderes Versprechen zurückgibt, das schließlich aufgelöst wird, wird der .catch()Handler übersprungen. Wenn der .then()Handler jedoch ein Versprechen auslöst oder zurückgibt, das schließlich .catch()abgelehnt wird, führt der Handler sowohl eine Ablehnung im ursprünglichen Versprechen pals auch einen Fehler aus, der im .then()Handler auftritt .

Im zweiten Schema wird beim pAuflösen der .then()Handler aufgerufen. Wenn dieser .then()Handler ein Versprechen wirft oder zurückgibt, das schließlich abgelehnt wird, kann der .catch()Handler das nicht abfangen, da es sich in der Kette davor befindet.

Das ist also der Unterschied Nr. 1. Wenn der .catch()Handler AFTER ist, kann er auch Fehler im .then()Handler abfangen .

Was passiert, wenn pabgelehnt wird:

Wenn das Versprechen im ersten Schema pabgelehnt wird, wird der .then()Handler übersprungen und der .catch()Handler wird wie erwartet aufgerufen. Was Sie im .catch()Handler tun, bestimmt, was als Endergebnis zurückgegeben wird. Wenn Sie nur einen Wert vom .catch()Handler zurückgeben oder ein Versprechen zurückgeben, das schließlich aufgelöst wird, wechselt die Versprechen-Kette in den gelösten Zustand, weil Sie den Fehler "behandelt" und normal zurückgegeben haben. Wenn Sie ein abgelehntes Versprechen in den .catch()Handler werfen oder zurückgeben, bleibt das zurückgegebene Versprechen abgelehnt.

Wenn im zweiten Schema das Versprechen pabgelehnt wird, wird der .catch()Handler aufgerufen. Wenn Sie einen normalen Wert oder ein Versprechen zurückgeben, das schließlich vom .catch()Handler behoben wird (wodurch der Fehler "behandelt" wird), wechselt die Versprechen-Kette in den aufgelösten Zustand und der .then()Handler nach dem .catch()Aufruf des.

Das ist also der Unterschied Nr. 2. Wenn der .catch()Handler VORHER ist, kann er den Fehler behandeln und dem .then()Handler ermöglichen, weiterhin aufgerufen zu werden.

Wann welche zu verwenden:

Verwenden Sie das erste Schema, wenn Sie nur einen .catch()Handler möchten , der Fehler entweder im ursprünglichen Versprechen poder im .then()Handler abfangen kann, und eine Ablehnung von psollte den .then()Handler überspringen .

Verwenden Sie das zweite Schema, wenn Sie in der Lage sein möchten, Fehler im ursprünglichen Versprechen zu erkennen, pund möglicherweise (abhängig von den Bedingungen) zulassen, dass die Versprechen-Kette wie gelöst fortgesetzt wird, und so den .then()Handler ausführen .

Die andere Option

Es gibt eine weitere Option, um beide Rückrufe zu verwenden, an die Sie .then()wie folgt übergeben können :

 p.then(fn1, fn2)

Dies garantiert, dass nur einer von fn1oder fn2jemals aufgerufen wird. Wenn es aufgelöst wird p, fn1wird es aufgerufen. Wenn pabgelehnt, fn2wird aufgerufen. Keine Änderung des Ergebnisses in fn1kann jemals dazu führen, dass fn2man angerufen wird oder umgekehrt. Wenn Sie also unbedingt sicherstellen möchten, dass nur einer Ihrer beiden Handler aufgerufen wird, unabhängig davon, was in den Handlern selbst geschieht, können Sie verwenden p.then(fn1, fn2).

jfriend00
quelle
17
Die Frage bezieht sich speziell auf die Reihenfolge von .then()und .catch(), die Sie beantworten. Darüber hinaus geben Sie einige Tipps, wann Sie welche Reihenfolge verwenden sollen. Ich halte es für angebracht, eine dritte Option zu erwähnen, nämlich die Übergabe des Erfolgs- und des Fehlerbehandlungsprogramms an .then () . In diesem Fall wird höchstens ein Handler aufgerufen.
ArneHugo
7
@ArneHugo - Guter Vorschlag. Ich fügte hinzu.
jfriend00
Können wir also während der Promise-Verkettung .then .catch .catch .then Arten von Szenarien schreiben?
Kapil Raghuwanshi
@KapilRaghuwanshi, ja, Sie können es verwenden, um im Fehlerfall einen Standardwert zu übergeben. dh Promise.reject(new Error("F")).then(x => x).catch(e => {console.log(e); return [1]}).then(console.log)und Promise.resolve([2]).then(x => x).catch(e => [1]).then(console.log)
CervEd
1
@DmitryShvedov - Wie ich vermutet habe, ist das falsch .then(this.setState({isModalOpen: false})). Sie übergeben keinen Funktionsverweis auf, .then()sodass der Code in den Parens sofort ausgeführt wird (bevor das Versprechen aufgelöst wird). Es sollte sein .then(() => this.setState({isModalOpen: false})).
jfriend00
30

Die Antwort von jfriend00 ist ausgezeichnet, aber ich dachte, es wäre eine gute Idee, den analogen synchronen Code hinzuzufügen.

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

ähnelt dem synchronen:

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

Wenn iMightThrow()nicht werfen, then()wird gerufen. Wenn es wirft (oder wenn es then()selbst wirft), handleCatch()wird es aufgerufen. Beachten Sie, dass der catchBlock keine Kontrolle darüber hat, ob er thenaufgerufen wird oder nicht .

Andererseits,

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

ähnelt dem synchronen:

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

then()

In diesem Fall wird ausgeführt, wenn iMightThrow()nicht geworfen then()wird. Wenn es auslöst, muss entschieden werden handleCatch(), ob then()aufgerufen wird, denn wenn erneut handleCatch()geworfen wird, then()wird es nicht aufgerufen, da die Ausnahme sofort an den Anrufer geworfen wird. Wenn handleCatch()das Problem ordnungsgemäß behandelt werden kann, then()wird es aufgerufen.

akivajgordon
quelle
Dies ist eine gute Erklärung, aber Sie könnten die Waise then()in afinally{...}
tyskr
2
@ 82Tuskers, bist du sicher? Stecke ich then()in finally{...}, wäre es nicht falsch , auch genannt werden , wenn handleCatch()wirft? Denken Sie daran, dass mein Ziel darin bestand, analogen synchronen Code
anzuzeigen
Wenn wir also alle Fälle behandeln möchten, aber dennoch .then () verketten möchten, ist es am besten, .then (etwas tun) .catch (Protokollfehler und Aktualisierungsstatus) .then (etwas anderes tun) .catch (Protokollfehler) zu verwenden. wo wir versuchen an jedem Punkt zu fangen, aber auch weiterhin die stmnts ausführen?
Anna