Maison  >  Article  >  interface Web  >  Explication détaillée de la boucle d'événements dans JS et Node.js

Explication détaillée de la boucle d'événements dans JS et Node.js

小云云
小云云original
2017-12-13 09:30:471475parcourir

Le

dans event loopjs conduit au problème des résultats d'exécution différents lors de l'exécution de programmes avec setTimeout et Promise dans chrome et node, ce qui conduit au mécanisme event loop de Nodejs. Je vous ai donné une analyse détaillée des principes et de l'utilisation des événements dans JS et Node.js. J'espère que cela pourra vous aider.

console.log(1)
setTimeout(function() {
 new Promise(function(resolve, reject) {
 console.log(2)
 resolve()
 })
 .then(() => {
 console.log(3)
 })
}, 0)
setTimeout(function() {
 console.log(4)
}, 0)
// chrome中运行:1 2 3 4
// Node中运行: 1 2 4 3

Les résultats d'exécution de Chrome et Node sont différents, ce qui est très intéressant.

1. File d'attente des tâches en JS

Une fonctionnalité majeure du langage JavaScript est le thread unique, c'est-à-dire ne faire qu'une seule chose. Alors pourquoi JavaScript ne peut-il pas avoir plusieurs threads ? Cela peut améliorer l’efficacité.

Le fil de discussion unique de JavaScript est lié à son objectif. En tant que langage de script de navigateur, l'objectif principal de JavaScript est d'interagir avec les utilisateurs et de manipuler le DOM. Cela détermine qu'il ne peut être qu'un seul thread, sinon cela entraînera des problèmes de synchronisation très complexes. Par exemple, supposons que JavaScript ait deux threads en même temps. Un thread ajoute du contenu à un certain nœud DOM et l'autre thread supprime le nœud. Dans ce cas, quel thread le navigateur doit-il utiliser ?

Par conséquent, afin d'éviter toute complexité, JavaScript est monothread depuis sa naissance. Cela est devenu la fonctionnalité principale de ce langage et ne changera pas à l'avenir.

Afin de profiter de la puissance de calcul des processeurs multicœurs, HTML5 propose le standard Web Worker, qui permet aux scripts JavaScript de créer plusieurs threads, mais les threads enfants sont entièrement contrôlés par le principal. thread et ne doit pas faire fonctionner le DOM. Par conséquent, cette nouvelle norme ne modifie pas la nature monothread de JavaScript.

2. Boucle d'événements de file d'attente de tâches

Un seul thread signifie que toutes les tâches doivent être mises en file d'attente et la tâche précédente ne sera exécutée que lorsque cela termine une mission. Si la tâche précédente prend beaucoup de temps, la tâche suivante devra attendre.

Par conséquent, toutes les tâches peuvent être divisées en deux types, l'une est une tâche synchrone (synchrone) et l'autre est une tâche asynchrone (asynchrone). Les tâches synchrones font référence aux tâches mises en file d'attente pour exécution sur le thread principal. La tâche suivante ne peut être exécutée qu'après l'exécution de la tâche précédente ; les tâches asynchrones font référence aux tâches qui n'entrent pas dans le thread principal mais entrent dans la « file d'attente des tâches ». ce n'est que lorsque la « file d'attente des tâches » informe le thread principal qu'une tâche asynchrone peut être exécutée que la tâche entrera dans le thread principal pour être exécutée.

Plus précisément, le mécanisme de fonctionnement de l'exécution asynchrone est le suivant. (Il en va de même pour l'exécution synchrone, car elle peut être considérée comme une exécution asynchrone sans tâches asynchrones.)

Toutes les tâches synchrones sont exécutées sur le thread principal, formant une pile de contexte d'exécution. En plus du thread principal, il existe également une « file d'attente des tâches ». Tant que la tâche asynchrone a des résultats en cours d'exécution, un événement est placé dans la « file d'attente des tâches ». Une fois que toutes les tâches de synchronisation de la « pile d'exécution » ont été exécutées, le système lit la « file d'attente des tâches » pour voir quels événements s'y trouvent. Les tâches asynchrones correspondantes mettent fin à l'état d'attente, entrent dans la pile d'exécution et démarrent l'exécution. Le fil principal continue de répéter la troisième étape ci-dessus.

Tant que le thread principal est vide, il lira la "file d'attente des tâches". C'est le mécanisme d'exécution de JavaScript. Ce processus ne cesse de se répéter.

3. Minuterie setTimeout et setInterval

La fonction minuterie se compose principalement des fonctions setTimeout() et setInterval(), leurs les mécanismes de fonctionnement internes sont exactement les mêmes, la différence est que le code spécifié par le premier est exécuté une fois, tandis que le second est exécuté à plusieurs reprises.

setTimeout(fn,0) signifie spécifier une tâche à exécuter dans le premier temps d'inactivité disponible du thread principal, c'est-à-dire à exécuter le plus tôt possible. Il ajoute un événement à la fin de la « file d'attente des tâches », il ne sera donc exécuté que lorsque la tâche de synchronisation et les événements existants dans la « file d'attente des tâches » auront été traités. La norme

HTML5 précise la valeur minimale (intervalle le plus court) du deuxième paramètre de setTimeout(), qui ne doit pas être inférieure à 4 millisecondes. Si elle est inférieure à cette valeur, elle le sera automatiquement. augmenter. Avant cela, les anciens navigateurs fixaient l'intervalle minimum à 10 millisecondes. De plus, ces modifications du DOM (en particulier celles impliquant un nouveau rendu de page) ne sont généralement pas exécutées immédiatement, mais toutes les 16 millisecondes. À l'heure actuelle, l'effet de l'utilisation de requestAnimationFrame() est meilleur que celui de setTimeout().

Il convient de noter que setTimeout() insère uniquement l'événement dans la "file d'attente des tâches". Le thread principal doit attendre que le code actuel (pile d'exécution) ait fini de s'exécuter avant que le thread principal n'exécute la fonction de rappel. précise. Si le code actuel prend beaucoup de temps, cela peut prendre beaucoup de temps, il n'y a donc aucun moyen de garantir que la fonction de rappel sera exécutée à l'heure spécifiée par setTimeout().

4. Boucle d'événement de Node.js

L'interrogation des événements interroge principalement la file d'attente des événements. Le producteur d'événements met les événements en file d'attente et les met dans la file d'attente. Il y a un fil à l'autre extrémité de la file d'attente appelé consommateur d'événements, qui demandera en permanence s'il y a des événements dans la file d'attente. file d'attente.S'il y a des événements,Il sera exécuté immédiatement.Afin d'éviter que les opérations de blocage n'affectent la file d'attente de lecture du thread actuel pendant l'exécution, le thread consommateur d'événements confiera à un pool de threads d'effectuer spécifiquement ces opérations de blocage.

Le mécanisme du front-end Javascript et de Node.js est similaire à ce modèle d'interrogation d'événements. Certaines personnes pensent que Node.js est monothread, c'est-à-dire le modèle d'interrogation d'événements. Le consommateur d'événement est monothread. L'interrogation, que dois-je faire s'il y a une opération de blocage, ne bloque-t-elle pas l'exécution monothread actuelle ?

En fait, Node.js dispose également d'un pool de threads en bas. Le pool de threads est spécialement utilisé pour effectuer diverses opérations de blocage. Cela n'affectera pas l'interrogation des événements de file d'attente du thread principal à thread unique. une certaine exécution de tâche. Une fois l'opération terminée, le pool de threads servira de producteur d'événement et placera les résultats de l'opération dans la même file d'attente.

En bref, une boucle d'événement d'interrogation d'événements nécessite trois composants :

Event Queue, qui appartient au modèle FIFO. Une extrémité pousse les données d'événement et l'autre extrémité extrait les données d'événement. Les deux extrémités communiquent uniquement via cette file d'attente, qui est un couplage lâche asynchrone. Le fil d'interrogation de lecture de la file d'attente, le consommateur de l'événement et le protagoniste de la boucle d'événement. Un pool de threads distinct, Thread Pool, est spécialement utilisé pour effectuer des tâches longues, des tâches lourdes et un travail physique intense.

Node.js est également une boucle d'événement à thread unique, mais son mécanisme de fonctionnement est différent de l'environnement du navigateur.

D'après la figure ci-dessus, le mécanisme de fonctionnement de Node.js est le suivant.

Le moteur V8 analyse les scripts JavaScript. Le code analysé appelle l'API Node. La bibliothèque libuv est responsable de l'exécution de l'API Node. Il alloue différentes tâches à différents threads pour former une boucle d'événement (boucle d'événement), et renvoie les résultats d'exécution des tâches au moteur V8 de manière asynchrone. Le moteur V8 renvoie ensuite les résultats à l'utilisateur.

Nous pouvons voir que le cœur de node.js est en fait la bibliothèque libuv. Cette bibliothèque est écrite en C et peut utiliser la technologie multi-threading, tandis que notre application Javascript est monothread.

Le processus d'exécution des tâches asynchrones de Nodejs :

Le code écrit par l'utilisateur est monothread, mais nodejs n'est pas monothread en interne !

Mécanisme d'événement :

Node.js n'utilise pas plusieurs threads pour effectuer le travail pour chaque requête, au lieu de cela, il ajoute tout le travail à une file d'attente d'événements, puis là un séparé. thread pour parcourir les événements de la file d’attente. Le thread de boucle d'événements récupère l'entrée supérieure de la file d'attente des événements, l'exécute, puis récupère l'entrée suivante. Lors de l'exécution de code d'E/S de longue durée ou bloquant

Dans Node.js, car il n'y a qu'un seul thread qui interroge constamment la file d'attente pour les événements, pour les opérations d'E/S telles que les systèmes de fichiers de base de données, y compris les requêtes HTTP et d'autres opérations faciles à bloquer et à attendre, si elles sont également implémentées dans ce thread unique, elles bloqueront définitivement l'exécution d'autres tâches. Javascript/Node.js le déléguera au pool de threads sous-jacent pour exécution et le fera. dire au pool de threads Une fonction de rappel, afin que le thread unique continue à effectuer d'autres choses. Lorsque ces opérations de blocage sont terminées, les résultats sont mis dans la file d'attente avec la fonction de rappel fournie. Lorsque le thread unique continue de lire les événements du. file d'attente, ces opérations bloquées sont lues. Après avoir exploité les résultats, ces résultats d'opération seront utilisés comme paramètres d'entrée de la fonction de rappel, puis la fonction de rappel sera activée pour s'exécuter.

Veuillez noter que le thread unique de Node.js est non seulement responsable de la lecture des événements de file d'attente, mais également de l'exécution de la fonction de rappel. C'est une fonctionnalité majeure qui le distingue du mode multi-thread. mode, un seul thread Le thread est uniquement responsable de la lecture des événements de file d'attente, et ne fait plus autre chose. Il confiera à d'autres threads de faire d'autres choses, notamment dans le cas du multicœur, un cœur de processeur est responsable de la lecture des événements de file d'attente. , et un cœur de processeur est responsable de l'exécution des tâches activées. De cette manière, idéal pour les tâches gourmandes en CPU. À son tour, la tâche d'activation d'exécution de Node..js, c'est-à-dire la tâche de la fonction de rappel, est toujours exécutée dans le thread unique responsable de l'interrogation, ce qui est destiné à l'empêcher d'effectuer des tâches gourmandes en ressources CPU, telles que la conversion. JSON vers d'autres formats de données, etc. , ces tâches affecteront l'efficacité de l'interrogation des événements.

5. Fonctionnalités de Nodejs

Les principales fonctionnalités de NodeJS : mécanisme asynchrone, piloté par les événements.

L'ensemble du processus d'interrogation des événements ne bloque pas la connexion des nouveaux utilisateurs et il n'est pas nécessaire de maintenir la connexion. Sur la base de ce mécanisme, en théorie, NodeJS peut répondre aux utilisateurs qui demandent des connexions les unes après les autres. Par conséquent, NodeJS peut prendre en charge une concurrence plus élevée que les programmes Java et PHP.

Bien que la maintenance de la file d'attente des événements nécessite également des coûts, et comme NodeJS est monothread, plus la file d'attente des événements est longue, plus il faut de temps pour obtenir une réponse, et le degré de concurrence sera toujours insuffisant.

RESTful API是NodeJS最理想的应用场景,可以处理数万条连接,本身没有太多的逻辑,只需要请求API,组织数据进行返回即可。

6. 实例

看一个具体实例:

console.log('1')
setTimeout(function() {
 console.log('2')
 new Promise(function(resolve) {
 console.log('4')
 resolve()
 }).then(function() {
 console.log('5')
 })
 setTimeout(() => {
 console.log('haha')
 })
 new Promise(function(resolve) {
 console.log('6')
 resolve()
 }).then(function() {
 console.log('66')
 })
})
setTimeout(function() {
 console.log('hehe')
}, 0)
new Promise(function(resolve) {
 console.log('7')
 resolve()
}).then(function() {
 console.log('8')
})
setTimeout(function() {
 console.log('9')
 new Promise(function(resolve) {
 console.log('11')
 resolve()
 }).then(function() {
 console.log('12')
 })
})
new Promise(function(resolve) {
 console.log('13')
 resolve()
}).then(function() {
 console.log('14')
})
// node1 : 1,7,13,8,14,2,4,6,hehe,9,11,5,66,12,haha // 结果不稳定
// node2 : 1,7,13,8,14,2,4,6,hehe,5,66,9,11,12,haha // 结果不稳定
// node3 : 1,7,13,8,14,2,4,6,5,66,hehe,9,11,12,haha // 结果不稳定
// chrome : 1,7,13,8,14,2,4,6,5,66,hehe,9,11,12,haha


chrome的运行比较稳定,而node环境下运行不稳定,可能会出现两种情况。

chrome运行的结果的原因是Promiseprocess.nextTick()的微任务Event Queue运行的权限比普通宏任务Event Queue权限高,如果取事件队列中的事件的时候有微任务,就先执行微任务队列里的任务,除非该任务在下一轮的Event Loop中,微任务队列清空了之后再执行宏任务队列里的任务。

相关推荐:

Node.js事件循环教程

javascript事件循环之强制梳理

深入理解Node.js 事件循环和回调函数

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:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn