web-dev-qa-db-fra.com

Mise en place des prises AVANT et APRES puis

J'ai du mal à comprendre la différence entre mettre .catch AVANT et APRÈS dans une promesse imbriquée.

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

Le comportement de chaque fonction est le suivant: test1 échoue si numéro correspond à <0 Test2 échoue si numéro correspond à > 10 Et test3 échoue si numéro n'est pas 100. Dans ce cas, test2 n'échoue que.

J'ai essayé d'exécuter et de faire échouer test2Async, BEFORE et APRÈS se comportent alors de la même manière, ce qui n'exécute pas test3Async. Quelqu'un peut-il m'expliquer la différence principale entre placer des prises à différents endroits?

Dans chaque fonction, je console.log('Running test X') afin de vérifier si elle est exécutée.

Cette question se pose à cause du fil précédent que j'ai posté Comment transformer un rappel imbriqué en promesse? . Je pense que c'est un problème différent et qu'il vaut la peine de poster un autre sujet.

76
Zanko

Donc, fondamentalement, vous demandez quelle est la différence entre ces deux (où p est une promesse créée à partir d'un code précédent):

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

et

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

Il existe des différences lorsque p est résolu ou rejeté, mais l’importance de ces différences dépend du comportement du code contenu dans les gestionnaires .then() ou .catch().

Que se passe-t-il quand p résout:

Dans le premier schéma, lorsque p est résolu, le gestionnaire .then() est appelé. Si ce gestionnaire .then() renvoie une valeur ou une autre promesse qui résout, le gestionnaire .catch() est ignoré. Mais, si le gestionnaire .then() lève ou retourne une promesse qui finit par être rejetée, le gestionnaire .catch() s'exécutera à la fois comme un rejet dans la promesse initiale p, mais également une erreur qui se produit dans le gestionnaire .then().

Dans le second schéma, lorsque p est résolu, le gestionnaire .then() est appelé. Si ce gestionnaire .then() lève ou retourne une promesse qui sera finalement rejetée, le gestionnaire .catch() ne pourra pas intercepter cela car il se trouve avant dans la chaîne.

Donc, c'est la différence # 1. Si le gestionnaire .catch() est APRÈS, il peut également intercepter des erreurs à l'intérieur du gestionnaire .then().

Que se passe-t-il quand p rejette:

Dans le premier schéma, si la promesse p rejette, le gestionnaire .then() est ignoré et le gestionnaire .catch() sera appelé comme vous le souhaitez. Ce que vous faites dans le gestionnaire .catch() détermine ce qui est renvoyé comme résultat final. Si vous renvoyez simplement une valeur à partir du gestionnaire .catch() ou une promesse qui est finalement résolue, la chaîne de promesses passe à l'état résolu parce que vous "avez manipulé" l'erreur et que vous l'avez renvoyée normalement. Si vous lancez ou retournez une promesse refusée dans le gestionnaire .catch(), la promesse renvoyée reste rejetée.

Dans le second schéma, si la promesse p rejette, le gestionnaire .catch() est appelé. Si vous renvoyez une valeur normale ou une promesse qui est finalement résolue par le gestionnaire .catch() (donc "traite" l'erreur), la chaîne de promesse bascule vers l'état résolu et le gestionnaire .then() après la .catch() sera appelée.

C'est donc la différence n ° 2. Si le gestionnaire .catch() est défini sur BEFORE, il peut alors gérer l'erreur et permettre au gestionnaire .then() de toujours être appelé.

Quand utiliser lequel:

Utilisez le premier schéma si vous voulez juste un gestionnaire .catch() capable de détecter les erreurs dans la promesse initiale p ou dans le gestionnaire .then() et un rejet de p devrait ignorer le gestionnaire .then().

Utilisez le second schéma si vous voulez être capable d’attraper les erreurs dans la promesse initiale p et peut-être (en fonction des conditions) permettre à la chaîne de promesses de continuer comme résolue, exécutant ainsi la .then() gestionnaire.

L'autre option

Il y a une autre option pour utiliser les deux callbacks que vous pouvez passer à .then() comme dans:

 p.then(fn1, fn2)

Cela garantit qu'un seul des fn1 Ou fn2 Sera appelé. Si p est résolu, alors fn1 Sera appelé. Si p rejette, alors fn2 Sera appelé. Aucun changement de résultat dans fn1 Ne peut jamais faire appeler fn2 Ou vice versa. Donc, si vous voulez être absolument certain qu'un seul de vos deux gestionnaires est appelé, peu importe ce qui se passe dans les gestionnaires eux-mêmes, vous pouvez utiliser p.then(fn1, fn2).

168
jfriend00

La réponse de jfriend est excellente, mais j'ai pensé que ce serait une bonne idée d'ajouter le code analogue synchrone.

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

est similaire au synchrone:

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

Si iMightThrow() ne lance pas, then() sera appelé. Si elle jette (ou si then() elle-même jette), alors handleCatch() sera appelée. Notez que le bloc catch n'a aucun contrôle sur le fait que then soit appelé ou non.

D'autre part,

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

est similaire au synchrone:

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

then()

Dans ce cas, si iMightThrow() ne lance pas, alors then() sera exécutée. Si elle jette, alors c’est à handleCatch() de décider si then() est appelée, parce que si handleCatch() retouche, alors then() ne sera pas appelé, étant donné que l'exception sera immédiatement renvoyée à l'appelant. Si handleCatch() peut gérer le problème avec élégance, alors then() sera appelé.

17
akivajgordon