Maison > interface Web > js tutoriel > Similitudes, différences et mécanismes partiels d'EventLoop entre les navigateurs et NodeJS

Similitudes, différences et mécanismes partiels d'EventLoop entre les navigateurs et NodeJS

不言
Libérer: 2018-07-07 17:23:57
original
2074 Les gens l'ont consulté

Cet article présente principalement les similitudes, les différences et certains mécanismes d'EventLoop entre les navigateurs et NodeJS. Il a une certaine valeur de référence. Maintenant, je le partage avec vous. Les amis dans le besoin peuvent s'y référer

. Similitudes et différences du navigateur avec EventLoop de NodeJS et certains mécanismes

javascript est un langage de script monothread, bien qu'il soit monothread, il dispose de nombreuses API asynchrones pour aider les développeurs à résoudre les problèmes de blocage de threads. Par exemple : fonction de rappel enregistrée onClick, ajax essentiel, etc... Mais comment l'environnement d'exécution JavaScript parvient-il à réaliser un thread unique sans bloquer le thread tout le temps et sans attendre la fin de diverses opérations asynchrones avant de continuer à exécuter l'opération ?
La réponse est : boucle d'événement

1.event loop 的规范是在HTML5中规定的。
2.event loop 是 javascript 运行环境(手动加粗) 的机制。
3.浏览器实现的event loop 与 NodeJS 实现的event loop 是有异同的。
Copier après la connexion
Lien de spécification de boucle d'événement défini en HTML5 https://www.w3.org/TR/html5/w...


La boucle d'événements d'un navigateur

1. Comprenez brièvement la

boucle d'événements, qui est la boucle d'événements. Quelle est sa structure ? Le blog du professeur Ruan Yifeng a une image, bien qu'elle soit très simple et claire, mais il manque certaines choses et ne peut pas démontrer pleinement le mécanisme global de circulation de la boucle d'événements. Jetons d'abord un coup d'œil aux photos :
Similitudes, différences et mécanismes partiels dEventLoop entre les navigateurs et NodeJS

Les images ne sont pas originales de l'auteur et proviennent du blog de Ruan Yifeng. Veuillez noter qu'elles ont été violées et. supprimé.

Les informations que nous pouvons obtenir de l'image sont :

1. Le moteur javascript exécute javascript dans un seul thread, car il n'y a qu'une seule pile dans laquelle il y a. divers processus d'exécution et d'attente.
2. Certaines webAPI placent le rappel généré lors de l'exécution dans une file d'attente, c'est-à-dire la "file d'attente des événements".
3. Dans la boucle d'événements, les événements en attente d'exécution dans la "file d'attente des événements" sont continuellement poussés dans la pile d'exécution javascript.

C'est le mécanisme de simplification des boucles d'événements. Pourquoi dit-on qu'il est simplifié ? Car il existe de nombreuses opérations et règles qui ne sont pas mentionnées dans la boucle.

Je ne donnerai pas de châtaignes, mais je vais donner une analogie.

Similitudes, différences et mécanismes partiels dEventLoop entre les navigateurs et NodeJS

Similitudes, différences et mécanismes partiels dEventLoop entre les navigateurs et NodeJS

Parlons d'une question courante (ce n'est pas pratique de modifier l'article, donc ce n'est qu'une ligne. Si vous changez de ligne, vous êtes les bienvenus) Frappez-moi ! )

setTimeout(e=>{ console.log(1) },0);

new Promise((res,rej)=>{ res() }).then(e=>{ console.log(2) });
Copier après la connexion

Ce sont également des API asynchrones fournies en javascript, et elles sont également exécutées directement (ce que les développeurs espèrent faire, même si cela entraînera des retards dus au blocage pour éviter la triche), mais peu importe qui entre ou sort de ces deux lignes de code, le résultat sera 2 1. Parce que cela implique l'ordre d'exécution et les règles de la macro-tâche et de la micro-tâche dans la boucle d'événements.


2. Processus global
Pour en revenir au problème que l'organigramme que je viens de mentionner n'est pas assez parfait, voici un organigramme de boucle d'événement complet et exhaustif.
Similitudes, différences et mécanismes partiels dEventLoop entre les navigateurs et NodeJS

L'image n'est pas originale par l'auteur et provient des secrets de javascript ninja. Veuillez noter qu'elle sera violée et supprimée.

Ceci est un organigramme complet d'une boucle d'événement. Sur l'image, nous pouvons voir de nombreux noms qui n'ont pas été mentionnés tout à l'heure. Trions-le du début à la fin. vers le bas) :

1. Lisez les tâches dans la file d'attente Macrotask. Il existe deux situations :
  • La file d'attente des tâches est vide, et la file d'attente des tâches d'exécution descendante

  • n'est pas vide, et la première entrée un (manuel + gras dans l'article) La tâche est poussée dans la pile d'exécution javascript et exécutée vers le bas

2. Lisez les tâches dans la file d'attente Microtask. Il existe deux situations :
  • La file d'attente des tâches est vide, et l'exécution descendante est

  • La file d'attente des tâches n'est pas vide, et la première la tâche saisie est poussée dans la pile d'exécution javascript, et répétez cette opération à nouveau (manuel + article en gras) jusqu'à ce que la file d'attente Microtask soit vide. Pour le dire franchement : Poussez toutes les tâches de cette file d'attente de tâches dans la pile d'exécution javascript dans l'ordre et exécutez vers le bas

Selon cette fois. Temps de cycle (manuel + gras dans l'article) Déterminer si a besoin de et si peut mettre à jour l'interface utilisateur [Je mentionnerai ce problème de temps de cycle plus tard]
  • Pas besoin, répétez la première étape

  • Besoin, passez à l'étape suivante

4. rendu, et en même temps bloque l'exécution de javascript. Et continuez à répéter la première étape.

Ce qui précède est l'ensemble du processus de boucle d'événements. À partir du processus, nous pouvons voir qu'il existe deux "files d'attente de tâches". L'API pour instancier ces deux files d'attente en javascript est

Macrotask queue --> setTimeout || setInterval || javascript代码

Microtask queue --> Promise.then()
Copier après la connexion

Ceci. termine l’ensemble du processus de boucle d’événements.

3.实例解析
什么鬼?这么复杂? 弄懂?不存在的
Similitudes, différences et mécanismes partiels dEventLoop entre les navigateurs et NodeJS

现在回到刚才提到的 “老生常谈的问题” 从实例的角度来说明一下问题。我们假设这个 javascript 文件叫做 "main.js"
"main.js"中的代码(+ 为自定义标记)

+1 console.log(1);

+2 setTimeout(e=>{ console.log(2); },0)

+3 setTimeout(e=>{ console.log(3); },0)

+4 new Promise((resolve,reject)=>{ console.log(4); resolve();})
.then(e=>{ console.log(5); })

+5 setTimeout(e=>{ console.log(6);

  +6 new Promise((resolve,reject)=>{ console.log(7); resolve(); })
     .then(e=>{ console.log(8);})
})
Copier après la connexion

那么这个执行顺序是怎样呢?从头带尾梳理一遍(词穷,全文只要是流程统一是“从头到尾梳理一遍”)

macrotask: javascript 代码,所有同步代码执行。输出:1 4。注册 +4 到 microtask。 注册+2 +3 +5 到 macrotask。
microtask: 执行 +4 输出:5
macrotask: 执行 +2。 输出 2
microtask:
macrotask: 执行 +3。 输出 3
microtask:
macrotask: 执行 +5。 输出 6 7。 注册 +6 到 microtask。
microtask: 输出 8

所以总体输出的顺序为:1 4 5 2 3 6 7 8

如果这个输出与你所想相同,那么基本就没有问题了。
那么如果不对或者有问题怎么办?
Similitudes, différences et mécanismes partiels dEventLoop entre les navigateurs et NodeJS

PS: 前面提到 【本次循环耗时】这个问题,这里我也不是非常清楚,望大牛指点。浏览器一般渲染页面60/S,以达到每秒60帧(60 fps),所以大概16ms一次,既然有了时间我们不经就会问?前面的任务处理耽误了则么办?因为javascript线程与UI线程互斥,某些任务导致 javascript引擎 坑了队友,自然而然没法在16ms的节点上到达这一步,从secrets of javascript ninja中了解到,一般会摒弃这次渲染,等待下一次循环。( 如有问题请指正! )

浏览器中的 event loop 到此结束,下面说说 NodeJS 的 event loop



二 NodeJS的event loop

NodeJS 的 event loop 也是有 Macrotask queue 与 Microtask queue 的。只不过 NodeJS 的略有不同。那么主要说说不同在哪里。

NodeJS中 Macrotask queue 与 Microtask queue 实例化到API为:

Macrotask queue --> script(主程序代码),setImmediate, I/O,setTimeout, setInterval

Microtask queue --> process.nextTick, Promise
Copier après la connexion

1.Macrotask queue 不同之处

上面说到了浏览器 event loop 的 Macrotask queue 在每次循环中只会读取一个任务,NodeJS 中 Macrotask queue 会一次性读取完毕( 同阶段的执行完毕,后面会说到Macrotask queue 分为 6个阶段 ),然后向下读取Microtask。

注意: 这一条与 NodeJS版本有很大关系,在看 深入浅出NodeJS 这一本书时( 看的版本很旧,不知是否有修订版,如有请告知。 ),提到的 setImmediate 每次循环只会执行一次,并且给出的示例在 v8.9.1 版本跑时已不符合书中所写。书中示例如下(+ 为自定义标记,原文中没有):
Copier après la connexion
+1 process.nextTick(function () {
       console.log('nextTick执行1');
   });

+2 process.nextTick(function () {
       console.log('nextTick执行2');
   });

+3 setImmediate(function () {
       console.log('setImmediateჽ执行1');

    +4 process.nextTick(function () {
           console.log('强势插入');
       });
   });

+5 setImmediate(function () {
       console.log('setImmediateჽ执行2');
   });

+6 console.log('正常执行');

正常执行
nextTick执行1
nextTick执行2
setImmediate执行1
强势插入
setImmediateჽ执行2
Copier après la connexion

在 v8.9.1 中截图如下
Similitudes, différences et mécanismes partiels dEventLoop entre les navigateurs et NodeJS

从图片中可以看到,至少在 v8.9.1 版本中 Macrotask queue 会直接全部执行。按照惯例从头到尾的梳理一遍

macrotask: javascript 代码,所有同步代码执行。输出:正常执行。注册 +3 +5 到 Macrotask。执行process.nextTick(),最终输出:正常执行, nextTick执行1, nextTick执行2。
  **microtask: 无
macrotask: 执行 +3 +5。 输出:setImmediate执行1, setImmediateჽ执行2。 执行process.nextTick(),最终输出:setImmediate执行1, setImmediateჽ执行2,强势插入。
microtask:

所以最终输出为:正常执行, nextTick执行1, nextTick执行2,setImmediate执行1, setImmediateჽ执行2,强势插入。


2.process.nextTick(),setImmediates,以及event loop的6个阶段

NodeJS 中 Macrotask queue会分为 6 个阶段,每个阶段的作用如下(process.nextTick()在6个阶段结束的时候都会执行):

timers:执行setTimeout() 和 setInterval()中到期的callback。

I/O callbacks:上一轮循环中有少数的I/Ocallback会被延迟到这一轮的这一阶段执行

idle, prepare:仅内部使用

poll:最为重要的阶段,执行I/O callback,在适当的条件下会阻塞在这个阶段

check:执行setImmediate的callback

close callbacks:执行close事件的callback,例如socket.on("close",func)
Copier après la connexion
注:此6个阶段非笔者原创来自 https://cnodejs.org/topic/5a9...,文章从底层C代码分析NodeJS event loop。这里做只做简单整合。侵删。

在了解了这六个阶段后,我们可以发现定时器系列在NodeJS event loop中 Macrotask queue 读取顺序为:

1. setTimeout(fun,0) setInterval(fun,0) 
2. setImmediate
Copier après la connexion

空口无凭,在实例中了解。的代码奉上( 代码较长,分为三段,方便阅读,避免滚动。 ):

+1 process.nextTick(function(){
    console.log("1");
});
+2 process.nextTick(function(){
    console.log("2");
    +3 setImmediate(function(){
        console.log("3");
    });
    +4 process.nextTick(function(){
        console.log("4");
    });
});

+5 setImmediate(function(){
    console.log("5");
    +6 process.nextTick(function(){
        console.log("6");
    });
    +7 setImmediate(function(){
        console.log("7");
    });
});
Copier après la connexion
+8 setTimeout(e=>{
    console.log(8);
    +9 new Promise((resolve,reject)=>{
        console.log(8+"promise");
        resolve();
    }).then(e=>{
        console.log(8+"promise+then");
    })
},0)

+10 setTimeout(e=>{ console.log(9); },0)

+11 setImmediate(function(){
    console.log("10");
    +12 process.nextTick(function(){
        console.log("11");
    });
    +13 process.nextTick(function(){
        console.log("12");
    });
    +14 setImmediate(function(){
        console.log("13");
    });
});
Copier après la connexion
console.log("14");

+15 new Promise((resolve,reject)=>{
    console.log(15);
    resolve();
}).then(e=>{
    console.log(16);
})
Copier après la connexion

这么复杂的异步嵌套在一起是不是很头疼呢?
我!不!看!了!

Similitudes, différences et mécanismes partiels dEventLoop entre les navigateurs et NodeJS

Le dernier peignage, le peignage le plus grand et le plus complet. Depuis l'Antiquité, triez-le du début à la fin

macrotâche : code javascript, toute exécution de code synchrone. Sortie : 14. Exécutez process.nextTick(), résultat final : 14, 15, 1, 2, 4. Enregistrez +3 +5 +8 +11 sur Macrotask. Inscrivez-vous +15 à Microtask.
microtâche : exécuter +15 sortie 16
macrotâche : exécuter +8 +10 sortie 8, 8promise, 9. Inscrivez-vous +9 à Microtask.
microtâche : exécuter +9 sortie 8promise+then
macrotâche : exécuter +5 +11 +3 sortie 5, 10, 3. Inscrivez-vous +7 +14 à macrotâche. Exécutez process.nextTick(), résultat final : 5 10 3 6 11 12.
microtâche :Aucune
macrotâche :Exécution +7 +14. Sortie : 7, 13
microtâche :Aucune

La sortie finale est : 14, 15, 1, 2, 4, 8, 8promise , 9, 8promesse+puis, 5, 10, 3, 6, 11, 12, 7, 13



Trois fin

C'est ça. Les boucles du navigateur et des événements NodeJS ont toutes été analysées. Au cours du processus, une partie du contenu du blog de Ruan Yifeng, de Zhihu et des articles du CSDN a été citée et supprimée.

Récemment, j'ai beaucoup gagné en comprenant certaines connaissances de base. Il s'agit notamment de... et de toutes sortes de questions étranges. Je les écrirai quand j'aurai le temps.

Ce qui précède représente l'intégralité du contenu de cet article. J'espère qu'il sera utile à l'étude de chacun. Pour plus de contenu connexe, veuillez faire attention au site Web PHP chinois !

Recommandations associées :

Utilisez javascript pour déterminer le type de navigateur

Utilisez Node pour fournir des services de fichiers statiques

Mécanisme de boucle d'événements du navigateur 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!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
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
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal