Cet article vous fera comprendre le mécanisme de boucle d'événement (boucle temporelle) dans Node.js. J'espère qu'il vous sera utile !
Aujourd'hui, nous allons découvrir la boucle d'événements dans nodeJs. La compréhension de la boucle événementielle a toujours été une grande difficulté pour moi. J'espère surmonter cette difficulté grâce à cette étude. J'espère également approfondir ma compréhension et mon impression de la boucle événementielle à travers ce blog.
Avant d'apprendre la boucle d'événements, comprenez d'abord la libuv du nœud. libuv est responsable de l'implémentation de différents modèles d'E/S sur différents systèmes d'exploitation et résume différentes implémentations dans des API pouvant être appliquées à des applications tierces.
Avant d'apprendre formellement la boucle d'événements, réfléchissons à une question
setTimeout(() => { console.log("timer1"); Promise.resolve().then(() => { console.log("promise1"); }); }, 0); setTimeout(() => { console.log("timer2"); Promise.resolve().then(() => { console.log("promise2"); }); }, 0);
Quel est le résultat de l'exécution de ce code dans le navigateur ?
Quel est le résultat de son exécution dans node ?
Avant node8.6 :
Après node8.6 :
Pourquoi il y a un tel résultat, nous l'analyserons plus tard !
phase des minuteries : exécute principalement les rappels setTimeOut, setInterval
phase de rappels en attente : exécute certaines erreurs d'appel système, telles que les rappels d'erreurs de communication réseau
phase d'inactivité/préparation : utilisée uniquement dans le système (nous avons aucun contrôle ni interférence à ce stade)
étape d'interrogation : obtenez de nouveaux événements d'E/S, comme l'obtention d'un rappel d'E/S pour la lecture d'un fichier.
Dans des circonstances appropriées, nodejs bloquera dans cette phasephase de vérification : exécutez le rappel setImmediate
Par exemple, exécutez la destory de sokect, fermez le rappel d'événement
Chaque phase suit un
FIFO) règles pour exécuter les tâches dans la file d'attente des tâches. Parmi ces six étapes, nous devons nous concentrer sur les étapes timers, poll, check. La plupart des tâches asynchrones de notre développement quotidien sont traitées au cours de ces trois étapes. timers
Pour l'incertitude ici, le site officiel donne un exemple :
Déclarez d'abord un setTimeOut, puis lisez un fichier en externe Lorsque l'opération de lecture du fichier dépasse le timer, l'opération de lecture du fichier définira le timer. est retardé. C'est la situation dans laquelle le thread principal n'est pas inactif comme mentionné précédemment.
poll
2 Lorsqu'un minuteur a expiré, exécutez sa fonction de rappel
. dans l'image ci-dessus, nous pouvons également voir :Après avoir exécuté les tâches de la file d'attente des tâches d'interrogation dans la phase d'interrogation, il vérifiera s'il existe un setImmediate prédéfini. S'il y en a, il entrera dans la phase de vérification. Sinon, nodejs. bloquera ici.
Ici, nous avons une question. S'il est bloqué lors de la phase de sondage, le minuteur que nous avons défini ne pourra-t-il pas être exécuté ? En faitLorsque la boucle d'événements est bloquée dans la phase d'interrogation, nodejs aura un mécanisme de vérification qui vérifiera si la file d'attente des minuteries est vide. Si elle n'est pas vide, elle entrera à nouveau dans la phase des minuteries.
check
event-loop的每个阶段都有一个队列,当event-loop达到某个阶段之后,将执行这个阶段的任务队列,直到队列清空或者达到系统规定的最大回调限制之后,才会进入下一个阶段。当所有阶段都执行完成一次之后,称event-loop完成一个tick。
上面我们说完了event-loop的理论部分,但是光有理论我们也还是不能很清晰的理解event-loop。下面我们就根据几个demo来更加深入的理解下event-loop!
demo1
const fs=require('fs') fs.readFile('test.txt',()=>{ console.log('readFile') setTimeout(()=>{ console.log('settimeout'); },0) setImmediate(()=>{ console.log('setImmediate') }) })
执行结果:
可见执行结果跟我们前面的分析时一致的!
demo2
const fs = require("fs"); const EventEmitter = require("events").EventEmitter; let pos = 0; const messenger = new EventEmitter(); messenger.on("message", function (msg) { console.log(++pos + " message:" + msg); // }); console.log(++pos + " first"); // process.nextTick(function () { console.log(++pos + " nextTick"); // }); messenger.emit("message", "hello!"); fs.stat(__filename, function () { console.log(++pos + " stat"); // }); setTimeout(function () { console.log(++pos + " quick timer"); // }, 0); setTimeout(function () { console.log(++pos + " long timer"); // }, 30); setImmediate(function () { console.log(++pos + " immediate"); // }); console.log(++pos + " last"); //
结果:
在node 8.6 之前:
浏览器中的微任务队列会在每个宏任务执行完成之后执行,而node中的微任务会在事件循环的各个阶段之间执行,即每个阶段执行完成之后会去执行微任务队列。
在8.6之后:
浏览器和node中微任务的执行是一致的!
所以,在文章开头,我们提出的思考的问题就有了结果。
语法:process.nextTick(callback,agrs)
执行时机:
这个函数其实是独立于 Event Loop 之外的,它有一个自己的队列,当每个阶段完成后,如果存在 nextTick 队列,就会清空队列中的所有回调函数,并且优先于其他 microtask 执行。递归的调用process.nextTick()
会导致I/O starving,官方推荐使用setImmediate()
关于starving现象的说明:
const fs = require("fs"); fs.readFile("test.txt", (err, msg) => { console.log("readFile"); }); let index = 0; function handler() { if (index >= 30) return; index++; console.log("nextTick" + index); process.nextTick(handler); } handler();
运行结果:
可以看到,等到nextTick函数呗执行30次之后,读取文件的回调才被执行!这样的现象被称为 I/O 饥饿。
当我们把 process.nextTick 换为 setImmediate
const fs = require("fs"); fs.readFile("test.txt", (err, msg) => { console.log("readFile"); }); let index = 0; function handler() { if (index >= 30) return; index++; console.log("nextTick" + index); setImmediate(handler); } handler();
结果:
造成这两种差异的原因是,嵌套调用的setImmediate的回调被排到了下一次event-loop中去!
通过今天的学习,让我对event-loop的理解更深刻了。那么,下次见。好好学习,天天向上!
更多编程相关知识,请访问:编程视频!!
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!