Maison  >  Article  >  interface Web  >  Explication détaillée du mécanisme de boucle d'événements JavaScript

Explication détaillée du mécanisme de boucle d'événements JavaScript

青灯夜游
青灯夜游avant
2018-10-08 15:30:432266parcourir

Cet article partage avec vous des points de connaissances pertinents sur le mécanisme de boucle d'événements JavaScript. Les amis intéressés peuvent l'apprendre et s'y référer.

Comme nous le savons tous, JavaScript est un langage monothread. Bien que Web-Worker ait été proposé en html5, cela ne change rien au fait que JavaScript soit monothread. Voir ce passage dans la spécification HTML :

Pour coordonner les événements, l'interaction utilisateur, les scripts, le rendu, la mise en réseau, etc., les agents utilisateurs doivent utiliser des boucles d'événements comme décrit dans cette section. de boucles d'événements : celles pour les contextes de navigation et celles pour les travailleurs

Afin de coordonner les événements, l'interaction des utilisateurs, les scripts, le rendu de l'interface utilisateur et le traitement du réseau, les moteurs utilisateur doivent utiliser des boucles d'événements. Event Loop se compose de deux types : un basé sur le contexte de navigation et un basé sur Worker, qui s'exécutent tous deux indépendamment. L'article suivant utilise un exemple pour se concentrer sur le mécanisme de boucle d'événements basé sur le contexte de navigation.

Regardez le code JavaScript suivant :

console.log('script start');

setTimeout(function() {
 console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
 console.log('promise1');
}).then(function() {
 console.log('promise2');
});

console.log('script end');

Devinez d'abord la séquence de sortie de ce code, puis entrez-la dans la console du navigateur pour voir la séquence de sortie réelle. Est-elle cohérente avec le. l'ordre que vous avez deviné ? S'il est cohérent, cela signifie que vous avez encore une certaine compréhension du mécanisme de boucle d'événements JavaScript. Continuez à lire pour consolider vos connaissances et si l'ordre de sortie réel est cohérent avec votre supposition. Sinon, alors la partie suivante ; de cet article répondra à vos questions.

File d'attente des tâches

Toutes les tâches peuvent être divisées en tâches synchrones et tâches asynchrones, comme leur nom l'indique, sont des tâches qui sont généralement exécutées immédiatement dans le thread principal. exécution ; Les tâches asynchrones sont des tâches exécutées de manière asynchrone, telles que les requêtes réseau ajax, les fonctions de synchronisation setTimeout, etc., qui sont toutes des tâches asynchrones qui sont coordonnées via le mécanisme de file d'attente des tâches (Event Queue). Plus précisément, vous pouvez utiliser la figure suivante pour expliquer grossièrement :

Les tâches synchrones et asynchrones entrent respectivement dans différents environnements d'exécution et entrent de manière synchrone dans le thread principal, c'est-à-dire l'exécution principale pile et entrez de manière asynchrone dans la file d’attente des événements. Si les tâches du thread principal sont vides après l'exécution, les tâches correspondantes seront lues dans la file d'attente d'événements et poussées vers le thread principal pour exécution. La répétition continue du processus ci-dessus est ce que nous appelons Event Loop.

Dans la boucle d'événement, chaque opération de boucle est appelée un tick. En lisant la spécification, on peut savoir que le modèle de traitement des tâches de chaque tick est relativement complexe. Les étapes clés peuvent être résumées comme suit :

  • Sélectionnez la tâche la plus ancienne qui entre dans la file d'attente en premier dans cette tick, s'il y en a une, exécutez-la (une fois)

  • Vérifiez si les microtâches existent, s'ils existent, continuez ensuite l'exécution jusqu'à ce que la file d'attente des microtâches soit effacée

  • Mettre à jour le rendu

Le thread principal répète les étapes ci-dessus

Vous pouvez utiliser une image pour illustrer le processus :

Je crois que quelqu'un voudra demander ici, que sont les microtâches ? Le cahier des charges stipule que les tâches sont divisées ? en deux catégories, à savoir Macro Task (macro tâche) et Micro Task (micro tâche), et une fois chaque macro tâche terminée, toutes les micro tâches doivent être effacées. La macro tâche ici est aussi ce que nous appelons souvent tâche. les distinguer. Les tâches mentionnées dans les articles suivants sont toutes considérées comme des macro-tâches.

La (macro)tâche comprend principalement : le script (code global), setTimeout, setInterval, I/O, les événements interactifs de l'interface utilisateur, setImmediate (environnement Node.js)

la microtâche comprend principalement : Promise, MutaionObserver, process.nextTick (environnement Node.js)

setTimeout/Promise et d'autres API sont des sources de tâches, et ce qui entre dans la file d'attente des tâches est la tâche d'exécution spécifique spécifiée par elles. Les tâches provenant de différentes sources de tâches entreront dans différentes files d'attente de tâches. Parmi eux, setTimeout et setInterval ont la même origine.

Analyse d'un exemple de code

Il y a des milliers de mots, il est donc préférable de l'expliquer clairement avec des exemples. Ci-dessous, nous pouvons suivre les spécifications et analyser l'exemple ci-dessus étape par étape. Tout d'abord, publiez l'exemple de code (pour vous éviter de faire défiler vers le haut).

console.log('script start');

setTimeout(function() {
 console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
 console.log('promise1');
}).then(function() {
 console.log('promise2');
});

console.log('script end');

Le script global entre dans le thread principal en tant que première tâche de macro, rencontre console.log et génère le démarrage du script

  • Rencontre setTimeout et sa fonction de rappel est distribué à la file d'attente d'événements de la macro-tâche

  • Lorsque Promise est rencontrée, sa fonction then est affectée à la file d'attente d'événements de la micro-tâche, enregistrée sous le nom then1, puis la fonction then est à nouveau rencontrée, et il est assigné Accédez à la file d'attente d'événements de microtâches, enregistrée sous le nom then2

  • Lorsque vous rencontrez console.log, fin du script de sortie

À ce stade, Event Queue Il y a trois tâches dans , comme indiqué dans le tableau suivant :

宏任务 微任务
setTimeout then1
- then2
  • 执行微任务,首先执行then1,输出 promise1, 然后执行 then2,输出 promise2,这样就清空了所有微任务

  • 执行 setTimeout 任务,输出 setTimeout 至此,输出的顺序是:script start, script end, promise1, promise2, setTimeout

so,你猜对了吗?

看看你掌握了没

再来一个题目,来做个练习:

console.log('script start');

setTimeout(function() {
 console.log('timeout1');
}, 10);

new Promise(resolve => {
 console.log('promise1');
 resolve();
 setTimeout(() => console.log('timeout2'), 10);
}).then(function() {
 console.log('then1')
})

console.log('script end');

这个题目就稍微有点复杂了,我们再分析下:

首先,事件循环从宏任务 (macrotask) 队列开始,最初始,宏任务队列中,只有一个 scrip t(整体代码)任务;当遇到任务源 (task source) 时,则会先分发任务到对应的任务队列中去。所以,就和上面例子类似,首先遇到了console.log,输出 script start; 接着往下走,遇到 setTimeout 任务源,将其分发到任务队列中去,记为 timeout1; 接着遇到 promise,new promise 中的代码立即执行,输出 promise1, 然后执行 resolve ,遇到 setTimeout ,将其分发到任务队列中去,记为 timemout2, 将其 then 分发到微任务队列中去,记为 then1; 接着遇到 console.log 代码,直接输出 script end 接着检查微任务队列,发现有个 then1 微任务,执行,输出then1 再检查微任务队列,发现已经清空,则开始检查宏任务队列,执行 timeout1,输出 timeout1; 接着执行 timeout2,输出 timeout2 至此,所有的都队列都已清空,执行完毕。其输出的顺序依次是:script start, promise1, script end, then1, timeout1, timeout2

用流程图看更清晰:

总结

有个小 tip:从规范来看,microtask 优先于 task 执行,所以如果有需要优先执行的逻辑,放入microtask 队列会比 task 更早的被执行。

最后的最后,记住,JavaScript 是一门单线程语言,异步操作都是放到事件循环队列里面,等待主执行栈来执行的,并没有专门的异步执行线程。

以上就是本章的全部内容,更多相关教程请访问JavaScript视频教程

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer