Les gestionnaires de promesses .then/.catch/.finally sont toujours asynchrones.
Même lorsquâune promesse est immédiatement résolue, le code sur les lignes situées ci-dessous .then/.catch/.finally sera toujours exécuté avant ces gestionnaires.
Voici la démo:
let promise = Promise.resolve();
promise.then(() => alert("promise done!"));
alert("code finished"); // cette alerte s'affiche d'abord
Si vous exécutez, vous voyez code finished dâabord, puis promise done!.
Câest étrange, car la promesse est certainement résolue depuis le début.
Pourquoi le .then se déclenche par la suite? Que se passe-t-il?
File dâattente pour micro-tâches
Les tâches asynchrones nécessitent une gestion appropriée. Pour cela, la norme ECMA spécifie une file dâattente interne PromiseJobs, plus souvent appelée âmicrotask queueâ en anglais (terme V8).
Comme indiqué dans la spécification:
- La file dâattente est premier entré, premier sorti: les tâches mises en file dâattente en premier sont exécutées en premier.
- Lâexécution dâune tâche est lancée uniquement lorsque rien dâautre nâest en cours dâexécution.
Ou, simplement, lorsquâune promesse est prête, ses gestionnaires .then/catch/finally sont mis en file dâattente ; ils ne sont pas encore exécutés. Lorsque le moteur JavaScript est libéré du code actuel, il extrait une tâche de la file dâattente et lâexécute.
Câest pourquoi âcode finishedâ dans lâexemple ci-dessus sâaffiche en premier.
Les gestionnaires de promesses passent toujours par cette file dâattente interne.
Sâil existe une chaîne avec plusieurs .then/catch/finally, chacun dâentre eux est exécuté de manière asynchrone. Câest-à -dire quâil est dâabord mis en file dâattente et exécuté lorsque le code actuel est terminé et que les gestionnaires précédemment placés en file dâattente sont terminés.
Et si lâordre importait pour nous ? Comment pouvons-nous faire en sorte que code finished apparaisse après promise done ?
Facile, il suffit de le mettre dans la file dâattente avec .then:
Promise.resolve()
.then(() => alert("promise done!"))
.then(() => alert("code finished"));
Maintenant, lâordre est comme prévu.
Rejet non traité
Souvenez-vous de lâévénement unhandledrejection du chapitre Gestion des erreurs avec des promesses ?
Maintenant, nous pouvons voir exactement comment JavaScript découvre quâil y a eu un rejet non géré
Un ârejet non traitéâ se produit lorsquâune erreur de promesse nâest pas traitée à la fin de la file dâattente des micro-tâches.
Normalement, si nous nous attendons à une erreur, nous ajoutons .catch dans la chaîne de promesse pour la gérer:
let promise = Promise.reject(new Error("Promise Failed!"));
promise.catch(err => alert('caught'));
// n'exécute pas: erreur gérée
window.addEventListener('unhandledrejection', event => alert(event.reason));
⦠Mais si nous oublions dâajouter .catch, dans ce cas le moteur déclenche lâévénement une fois que la file dâattente de micro-tâches est vide :
let promise = Promise.reject(new Error("Promise Failed!"));
// Promise Failed!
window.addEventListener('unhandledrejection', event => alert(event.reason));
Et si nous gérons lâerreur plus tard? Comme ceci:
let promise = Promise.reject(new Error("Promise Failed!"));
setTimeout(() => promise.catch(err => alert('caught')), 1000);
// Error: Promise Failed!
window.addEventListener('unhandledrejection', event => alert(event.reason));
Maintenant, si vous lâexécutez, nous verrons dâabord le message Promise Failed!, Puis caught.
Si nous ne connaissions pas la file dâattente de micro-tâches, nous pourrions nous demander : âPourquoi le gestionnaire unhandledrejection a-t-il été exécuté ? Nous avons capturé et géré lâerreur !â.
Mais nous comprenons maintenant que unhandledrejection est généré à la fin de la file dâattente des micro-tâches : le moteur examine les promesses et, si lâune dâentre elles est à lâétat ârejectedâ, lâévénement se déclenche.
Dans lâexemple ci-dessus, .catch ajouté par setTimeout se déclenche également, mais plus tard, après que unhandledrejection se soit déjà produit, mais cela ne change rien.
Résumé
Le traitement des promesses est toujours asynchrone, car toutes les actions de promesse passent par la file dâattente interne âpromise jobsâ, également appelée âmicrotask queueâ (terme V8).
Ainsi, les gestionnaires .then/catch/finally sont toujours appelés une fois le code actuel terminé.
Si nous devons garantir quâun morceau de code est exécuté après .then/catch/finally, nous pouvons lâajouter à un appel .then enchaîné.
Dans la plupart des moteurs JavaScript, y compris les navigateurs et Node.js, le concept de micro-tâches est étroitement lié à la âboucle dâévénementâ et aux âmacrotachesâ. Comme elles nâont pas de relation directe avec les promesses, elles sont décrites dans une autre partie du didacticiel, au chapitre La boucle d'événement: les microtâches et les macrotâches.
Commentaires
<code>, pour plusieurs lignes â enveloppez-les avec la balise<pre>, pour plus de 10 lignes - utilisez une sandbox (plnkr, jsbin, codepenâ¦)