web-dev-qa-db-fra.com

Quelle est la différence entre alors et finalement dans une promesse?

Je vois la documentation pour le finally de Bluebird, mais je ne comprends toujours pas très bien la différence entre then.

Pour être clair: je sais exactement pourquoi then est appelé après une catch. I veux qu'il soit appelé après une prise. C'est l'intention. Ma question est la suivante: si je veux le code doit toujours être exécuté quel que soit l'état de la promesse, quelle est la différence entre then et finally pour cela?

J'ai construit ce test:

var Promise = require("bluebird");

function test1 () {
    console.log("RESOLVE + THEN + CATCH + THEN");
    return new Promise((resolve, reject) => resolve())
       .then(() => console.log("then"))
       .catch(err => console.log("error:", err.message))
       .then(() => console.log("end"));
}

function test2 () {
    console.log("REJECT + THEN + CATCH + THEN");
    return new Promise((resolve, reject) => reject(new Error("rejected")))
       .then(() => console.log("then"))
       .catch(err => console.log("error:", err.message))
       .then(() => console.log("end"));
}

function test3 () {
    console.log("RESOLVE + THEN + CATCH + FINALLY");
    return new Promise((resolve, reject) => resolve())
       .then(() => console.log("then"))
       .catch(err => console.log("error:", err.message))
       .finally(() => console.log("end"));
}

function test4 () {
    console.log("REJECT + THEN + CATCH + FINALLY");
    return new Promise((resolve, reject) => reject(new Error("rejected")))
       .then(() => console.log("then"))
       .catch(err => console.log("error:", err.message))
       .finally(() => console.log("end"));
}

// run tests "sequentially" so console output doesn't get blended
setTimeout(test1, 500);
setTimeout(test2, 1000);
setTimeout(test3, 1500);
setTimeout(test4, 2000);

Ceci teste quatre cas:

  1. .then(...).catch(...).then(...) avec une promesse résolue.
  2. .then(...).catch(...).then(...) avec une promesse refusée.
  3. .then(...).catch(...).finally(...) avec une promesse résolue.
  4. .then(...).catch(...).finally(...) avec une promesse refusée.

Les résultats que je vois sont les cas 1 + 2 se comportent de manière identique à 3 + 4: le dernier bit (then ou finally en fonction du test) est exécuté quoi qu'il se passe avant, comme prévu. Le résultat de ce programme est:

RESOLVE + THEN + CATCH + THEN
then
end
REJECT + THEN + CATCH + THEN
error: rejected
end
RESOLVE + THEN + CATCH + FINALLY
then
end
REJECT + THEN + CATCH + FINALLY
error: rejected
end

Maintenant, la raison pour laquelle je pose la question est parce que j’ai vu un commenter cette autre question que j’ai posée }:

Vous n'êtes pas sûr que vos promesses le prennent en charge, mais vous devez remplacer le dernier .then par .finally afin que la variable busy soit toujours effacée.

D'après ma connaissance très limitée de then et les tests ci-dessus, il semble que then soit suffisant. Mais après ce commentaire, je me pose des questions sur moi-même et sur la sécurité de l’utilisation de then pour exécuter du code "enfin".

Ma question est donc la suivante: quelle est la différence entre then et finally? Ils look aiment bien se comporter de la même manière, mais quand aurais-je besoin d'utiliser finally au lieu de then?

4
Jason C

Première différence: parfois, vous ne voulez pas intercepter les erreurs à l’endroit où elles se produisent, mais dans le code qui utilise cette fonction pour ne pas les intercepter. Dans ce cas, vous ne pouvez pas remplacer then() et finally()

Parfois, vous devez nettoyer quelque chose, qu’il s’agisse d’une erreur ou non (annulation des références, suppression des délais impartis, etc.). C'est là que vous utilisez finally()

Deuxième différence: la fonction que vous transmettez à catch() peut également être lancée. Dans ce cas, votre promesse serait rejetée et la then() suivante ne serait pas appelée.

(donc, finalement, un catch exécutera toujours une erreur, je ne le savais pas)

Oui, c'est le point de finally(). Il sera exécuté en toutes circonstances sans changer la valeur résolue.

Vous voudrez peut-être lire/google un peu sur try {} finally {}, sans catch.

7
Thomas

.then et .finally ne sont pas identiques.

.then est la primitive de promesse principale. C'est ce qui est défini, à fond, dans Promises/A + spec et toutes les bibliothèques de promesses le mettront en œuvre.

Un Bluebird .finally handler sera "appelé quel que soit le sort de la promesse". Donc, une exception non gérée déclenche toujours un .finally.

new Promise((resolve, reject) => reject(false))
  .finally(a => console.log('finally', a))
// finally undefined
// Unhandled rejection false

new Promise((resolve, reject) => reject(false))
  .then(a => console.log('then', a))
// Unhandled rejection false

.finally ne changera pas la valeur résolue de la promesse et ne recevra pas le résultat de la chaîne de promesse. 

new Promise((resolve, reject) => reject(false))
  .catch(e => {
    console.log(e)
    return 2
  })
  .finally(a => {
    console.log('finally', a)
    return 1
  })
  .then(res => console.log('res', res))
// finally undefined
// res 2

Les méthodes ont une apparence similaire dans vos scénarios de test car les tests détectent toutes les erreurs et vous utilisez uniquement des promesses pour le contrôle de flux, sans vous fier aux valeurs résolues/rejetées le long de la chaîne de promesses.

3
Matt

D'accord, après avoir discuté et beaucoup d'aide de KevinB, j'ai découvert au moins une différence. Considérez les deux nouveaux tests suivants:

function test5 () {
    console.log("REJECT + THEN + CATCH/THROW + THEN");
    return new Promise((resolve, reject) => reject(new Error("rejected")))
       .then(() => console.log("then"))
       .catch(function(err) { throw new Error("error in catch"); })
       .then(() => console.log("end"));
}

function test6 () {
    console.log("REJECT + THEN + CATCH/THROW + FINALLY");
    return new Promise((resolve, reject) => reject(new Error("rejected")))
       .then(() => console.log("then"))
       .catch(function(err) { throw new Error("error in catch"); })
       .finally(() => console.log("end"));
}

Dans ceux-ci, la promesse est rejetée, mais une erreur est générée par catch.

La promesse finit par être rejetée dans les deux cas, mais pour le cas finally le finally est toujours exécuté, le then ne l’est pas.

Donc c'est la différence. Ils sont presque les mêmes, à la seule exception que, lorsqu'une erreur est générée par lecatchhandler, finally est exécuté et que then ne le fait pas.

Cela signifie que le commentaire que j'ai cité a également un intérêt: si dans mon gestionnaire d'erreurs une autre erreur se produisait, une variable then ne garantirait pas le nettoyage, mais une variable finally le ferait. C'est le cas qui me manquait.

1
Jason C