Im vorherigen Artikel haben wir erfahren, dass die Ereignisschleife ein wichtiger Bestandteil von Node.js ist und zur Koordinierung der Ausführung von synchronem und asynchronem Code verwendet wird.
Es besteht aus sechs verschiedenen Warteschlangen. Eine nextTick-Warteschlange und eine Promise-Warteschlange (Mikrotask-Warteschlange genannt), eine Timer-Warteschlange, eine I/O-Warteschlange, eine Prüfwarteschlange und schließlich die Shutdown-Warteschlange. [Empfohlene verwandte Tutorials: nodejs-Video-Tutorial, Programmierlehre]
In jeder Schleife wird die Rückruffunktion bei Bedarf aus der Warteschlange entfernt und auf dem Aufrufstapel ausgeführt. Ausgehend von diesem Artikel führen wir einige Experimente durch, um sicherzustellen, dass unser Verständnis der Ereignisschleife korrekt ist.
Unsere erste Reihe von Experimenten wird sich auf die nextTick-Warteschlange und die Promise-Warteschlange konzentrieren. Bevor wir uns jedoch auf das Experiment einlassen, müssen wir verstehen, wie Rückruffunktionen in die Warteschlange gestellt werden.
Hinweis: Alle Experimente wurden mit dem CommonJS-Modulformat durchgeführt.
Um die Rückruffunktion zur nextTick-Warteschlange hinzuzufügen, können wir die integrierte Methode process.nextTick()
verwenden. Die Syntax ist sehr einfach: process.nextTick(callbackFn)
. Wenn die Methode auf dem Aufrufstapel ausgeführt wird, wird die Rückruffunktion zur nextTick-Warteschlange hinzugefügt. process.nextTick()
方法。语法很简单:process.nextTick(callbackFn)
。当该方法在调用栈上执行时,回调函数将被加入到 nextTick 队列中。
要将回调函数加入到 Promise 队列中,我们需要使用 Promise.resolve().then(callbackFn)
。当 Promise 解决时(resolve),传递给 then()
的回调函数将被入队到 Promise 队列中。
现在我们已经了解了如何向两个队列添加回调函数,让我们开始第一个实验吧。
// index.js console.log("console.log 1"); process.nextTick(() => console.log("this is process.nextTick 1")); console.log("console.log 2");
这段代码,记录了三个不同的语句。第二个语句使用 process.nextTick()
将回调函数排入 nextTick 队列。
第一个 console.log()
语句被推入调用栈中执行。它在控制台中记录相应的消息,然后弹出堆栈。
接下来,在调用栈上执行 process.nextTick()
,将回调函数入队到 nextTick 队列,并弹出。此时仍有用户编写的代码需要执行,因此回调函数必须等待其轮到。
执行继续进行,最后一个 console.log()
语句被推入堆栈中,该消息记录在控制台中,并将该函数从调用栈中弹出。现在,没有更多的用户编写同步代码需要执行,因此控制权进入事件循环。
nextTick 队列中的回调函数被推入调用栈,console.log()
被推入调用栈并且执行,在控制台记录相应的消息。
console.log 1 console.log 2 this is process.nextTick 1
所有用户编写的同步 JavaScript 代码优先于异步代码执行。
让我们继续进行第二个实验。
// index.js Promise.resolve().then(() => console.log("this is Promise.resolve 1")); process.nextTick(() => console.log("this is process.nextTick 1"));
我们有一个 Promise.resolve().then() 调用和一个 process.nextTick() 调用。
当调用栈执行第 1 行时,它将回调函数排队到 Promise 队列中;当调用栈执行第 2 行时,它将回调函数排队到 nextTick 队列中。
第 2 行后没有代码需要执行。
控制权进入事件循环,在其中 nextTick 队列优先于 Promise 队列(这是 Node.js 运行时的工作方式)。
事件循环执行 nextTick 队列回调函数,然后再执行 Promise 队列回调函数。
控制台显示“this is process.nextTick 1”,然后是“this is Promise.resolve 1”。
this is process.nextTick 1 this is Promise.resolve 1
nextTick 队列中的所有回调函数优先于 Promise 队列中的回调函数执行。
让我带你走一遍上述第二个实验的更详细版本。
// index.js process.nextTick(() => console.log("this is process.nextTick 1")); process.nextTick(() => { console.log("this is process.nextTick 2"); process.nextTick(() => console.log("this is the inner next tick inside next tick") ); }); process.nextTick(() => console.log("this is process.nextTick 3")); Promise.resolve().then(() => console.log("this is Promise.resolve 1")); Promise.resolve().then(() => { console.log("this is Promise.resolve 2"); process.nextTick(() => console.log("this is the inner next tick inside Promise then block") ); }); Promise.resolve().then(() => console.log("this is Promise.resolve 3"));
该代码包含三个 process.nextTick()
调用和三个 Promise.resolve()
Promise.resolve().then(callbackFn)
verwenden. Wenn ein Promise aufgelöst wird, wird die an then()
übergebene Rückruffunktion in die Promise-Warteschlange eingereiht. 🎜🎜Da wir nun wissen, wie man Rückruffunktionen zu zwei Warteschlangen hinzufügt, beginnen wir mit unserem ersten Experiment. 🎜this is process.nextTick 1 this is process.nextTick 2 this is process.nextTick 3 this is the inner next tick inside next tick this is Promise.resolve 1 this is Promise.resolve 2 this is Promise.resolve 3 this is the inner next tick inside Promise then block
process.nextTick()
, um die Rückruffunktion in die nextTick-Warteschlange einzureihen. 🎜console.log The ()</ Die code>-Anweisung wird zur Ausführung in den Aufrufstapel verschoben. Es protokolliert die entsprechende Meldung in der Konsole und öffnet dann den Stapel. 🎜🎜Als nächstes führen Sie <code>process.nextTick()
auf dem Aufrufstapel aus, stellen die Rückruffunktion in die nextTick-Warteschlange ein und öffnen sie. Zu diesem Zeitpunkt muss noch vom Benutzer geschriebener Code ausgeführt werden, sodass die Rückruffunktion warten muss, bis sie an der Reihe ist. 🎜🎜Die Ausführung wird fortgesetzt, die letzte console.log()
-Anweisung wird auf den Stapel verschoben, die Nachricht wird in der Konsole protokolliert und die Funktion wird aus dem Aufrufstapel entfernt. Jetzt muss kein vom Benutzer geschriebener synchroner Code mehr ausgeführt werden, sodass die Steuerung in die Ereignisschleife übergeht. 🎜🎜Die Rückruffunktion in der nextTick-Warteschlange wird in den Aufrufstapel verschoben, console.log()
wird in den Aufrufstapel verschoben und ausgeführt und die entsprechende Nachricht wird in der Konsole aufgezeichnet. 🎜rrreee🎜Der gesamte vom Benutzer geschriebene synchrone JavaScript-Code wird vor asynchronem Code ausgeführt. 🎜🎜Lass uns mit dem zweiten Experiment fortfahren. 🎜
🎜Alle Callback-Funktionen in der nextTick-Warteschlange werden vor Callback-Funktionen in der Promise-Warteschlange ausgeführt. 🎜🎜Lassen Sie mich Sie durch eine detailliertere Version des zweiten Experiments oben führen. 🎜
process.nextTick()
-Aufrufe und drei Promise.resolve()
-Aufrufanweisungen. Jede Rückruffunktion protokolliert die entsprechende Nachricht. 🎜但是,第二个 process.nextTick()
和第二个 Promise.resolve()
都有一个额外的 process.nextTick()
语句,每个都带有一个回调函数。
为了加快对此可视化的解释,我将省略调用栈讲解。当调用栈执行所有 6 个语句时,nextTick 队列中有 3 个回调函数,Promise 队列中也有 3 个回调函数。没有其他要执行的内容后,控制权进入事件循环。
我们知道,nextTick 队列中具有高优先级。首先执行第 1 个回调函数,并将相应的消息记录到控制台中。
接下来,执行第 2 个回调函数,记录了第 2 条日志语句。但是,这个回调函数包含另一个对 process.nextTick()
的调用,该方法内的回调函数(译注:即() => console.log("this is the inner next tick inside next tick")
)入队到nextTick 队列末尾。
然后 Node.js 执行第 3 个回调函数并将相应消息记录到控制台上。最初只有 3 个回调,但是因为第 2 次时向nextTick 队列又添加了一个,所以变成 4 个了。
事件循环将第 4 个回调函数推入调用栈,执行 console.log()
语句。
接着处理完 nextTick 队列之后,控制流程进入到 Promise 队列。Promise 队列与 nextTick 队列处理方式类似。
首先记录“Promise.resolve 1”,然后是“Promise.resolve 2”,这时因为调用 process.nextTick() 的原因,一个函数(译注:即 () => console.log("this is the inner next tick inside Promise then block")
) 被推入 nextTick 队列了。尽管如此,控制流程仍停留在 Promise 队列,因此还会继续执行队列中的其他函数。然后我们得到“Promise.resolve 3”,此时 Promise 队列为空。
Node.js 将再次检查微任务队列中是否有新的回调。由于 nextTick 队列中有一个回调,因此会执行这个回调,导致我们的最后一条日志输出。
this is process.nextTick 1 this is process.nextTick 2 this is process.nextTick 3 this is the inner next tick inside next tick this is Promise.resolve 1 this is Promise.resolve 2 this is Promise.resolve 3 this is the inner next tick inside Promise then block
这可能是一个稍微高级的实验,但推论仍然相同。
nextTick 队列中的所有回调函数优先于 Promise 队列中的回调函数执行。
使用 process.nextTick()
时要小心。过度使用此方法可能会导致事件循环饥饿,从而阻止队列中的其余部分运行。当存在大量的 nextTick() 调用时,I/O 队列是无法执行自己的回调函数的。官方文档建议使用 process.nextTick()
的两个主要场景:处理错误或在调用栈为空事件循环继续之前执行回调用。所以在使用 process.nextTick()
时,要明智一些。
实验表明,用户编写的所有同步 JavaScript 代码优先于异步代码执行,并且 nextTick 队列中的所有回调函数优先于 Promise 队列中的回调函数执行。
原文链接:Visualizing nextTick and Promise Queues in Node.js Event Loop,2023年3月30日,by Vishwas Gopinath
更多node相关知识,请访问:nodejs 教程!
Das obige ist der detaillierte Inhalt vonEine eingehende Analyse der Mikrotask-Warteschlange in der Node-Ereignisschleife. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!