Je ne peux pas comprendre comment fonctionne le code suivant. Pourquoi "1" est après "b" mais "h" est après "3"? Faut-il que l'ordre soit: a, b, 1, 2, h, 3? Certains articles disaient que la différence entre "file d'attente de la boucle d'événement" et "file d'attente des travaux" conduit à la sortie suivante. Mais comment? J'ai lu la spécification de ECMAScript 2015 - 8.4 Emplois et files d'attente de travail , voulant savoir comment fonctionne Promise'job, mais cela me rend encore plus confus. Est-ce que quelqu'un peut m'aider? Je vous remercie!
var promise = new Promise(function(resolve, reject) {resolve(1)});
promise.then(function(resolve) {console.log(1)});
console.log('a');
promise.then(function(resolve) {console.log(2);});
setTimeout(function() {console.log('h')}, 0);
promise.then(function(resolve) {console.log(3)});
console.log('b');
// a
// b
// 1
// 2
// 3
// h
Je sais que Promise est asynchrone, mais le rappel de l'opération asynchrone setTimeout (..) se fait toujours après l'opération asynchrone de Promise. Pourquoi?
Pourquoi "1" est après "b"?
Par promesse, tous les gestionnaires .then()
de promise sont appelés de manière asynchrone APRÈS que le thread actuel de JS soit terminé. Ainsi, les deux variables a
et b
qui sont exécutées de manière synchrone dans le JS actuel seront exécutées avant tout gestionnaire .then()
, de sorte que 1
sera toujours après a
et b
.
Quelques lectures intéressantes: T demande, micro-tâches, files d'attente et horaires et Quel est l'ordre d'exécution dans les promesses javascript et Ecriture d'un framework JavaScript - Délai d'exécution, au-delà de setTimeout .
Il y a quelques bons conseils ici dans ce fil de discussion: Les promesses changent de chemin entre nextTick
et setImmediate
:
Je ne recommanderais pas de s'appuyer sur l'ordre d'exécution exact des événements Non chaînés. Si vous souhaitez contrôler l'ordre d'exécution - Réorganisez les rappels de manière à ce que celui que vous souhaitez exécuter ultérieurement dépende de celui que vous souhaitez exécuter plus tôt, ou implémenter une file d'attente (qui fait la même chose derrière le capot).
En d'autres termes, si vous dépendez d'un moment particulier des événements asynchrones, vous devez alors les chaîner dans votre code. Ainsi, l'un après l'autre via votre code plutôt que de vous fier à une planification non spécifiée dans la mise en œuvre.
En termes HTML, la boucle event pour une page ou un ensemble de pages du même domaine peut avoir plusieurs files d'attente task . Les tâches de la même source task vont toujours dans la même file d'attente, le navigateur choisissant la file d'attente à utiliser par la suite.
Les tâches pour exécuter des rappels de minuterie proviennent de la source de tâches timer et entrent dans la même file d'attente. Appelons cette file d'attente file d'attente de tâches "A" .
La spécification ECMAscript 2015 (ES6) nécessite que les tâches exécutent des rappels de réaction Promise pour former leur propre file d'attente appelée "PromiseJobs" . Les spécifications ECMAscript et HTML n'utilisant pas le même langage, nous allons donc assimiler théoriquement la "file d'attente des tâches Promise" d'ECMA à la file d'attente HTML "B" du navigateur - au moins une file d'attente différente de celle utilisée par les minuteries.
Théoriquement, un navigateur peut choisir d'exécuter des tâches dans la file d'attente A ou B, mais en pratique la file d'attente de tâches promise prend une priorité plus élevée et se vide avant qu'un rappel par minuterie ne soit exécuté.
C'est pourquoi "h" est enregistré en dernier. Les appels Promise then
sur les promesses remplies placent des travaux dans la file d'attente des promesses, qui sont exécutés avec une priorité plus élevée que les rappels par minuterie. La file d'attente de promesses ne devient vide qu'après l'exécution de console.log(3)
, ce qui permet à l'appel du timer de s'exécuter à nouveau.
Les gardiens ECMAScript ont choisi de ne pas utiliser la terminologie HTML5 ni la description des files d'attente de tâches dans leurs spécifications car ECMAScript peut s'exécuter dans davantage d'environnements que les seuls navigateurs HTML.
L'implémentation native des files d'attente de promesses peut utiliser une file d'attente "micro-tâches" au lieu d'une file d'attente dédiée distincte. Les travaux en micro-file d'attente sont simplement exécutés une fois le fil de script actuel et toutes les tâches précédemment ajoutées à la micro-file d'attente terminées.
Le détail de la mise en file d'attente de micro-tâches n'est pas nécessaire pour comprendre les promesses.
Les Polyfill de Promise pour les navigateurs dépourvus de prise en charge native des promesses (toutes les versions de IE etc.) peuvent utiliser des minuteries et ne pas se comporter exactement de la même manière que les implémentations natives en ce qui concerne l'ordre de réaction des promesses et les rappels de minuterie .
J'ai trouvé celui-ci facile à comprendre pour quelqu'un de nouveau chez JS.
Ceci est un copier/coller du livre de @ getify
pour utiliser une métaphore: la file d'attente de la boucle d'événement ressemble à une promenade dans un parc d'attractions, où une fois la randonnée terminée, vous devez vous rendre à l'arrière de la file pour recommencer. Mais la file d’attente de Job, c’est comme finir le trajet, puis couper en file et reprendre le fil.
file d'attente de la boucle d'événement - pour tous les rappels asynchrones autres que les promesses, h
file d'attente - pour tous les rappels asynchrones liés aux promesses. 1, 2, 3
Sync - a, b