이전 기사에서 마이크로태스크 대기열과 각 대기열 내 우선순위에 대해 논의했습니다. 이 기사에서는 비동기 코드를 처리하기 위한 Node.js의 또 다른 대기열인 타이머 대기열에 대해 설명합니다.
타이머 대기열에 대해 알아보기 전에 마이크로태스크 대기열에 대해 간단히 살펴보겠습니다. 콜백 함수를 마이크로태스크 대기열에 추가하기 위해 process.nextTick()
및 Promise.resolve()
와 같은 함수를 사용합니다. Node.js에서 비동기 코드를 실행할 때 마이크로태스크 대기열의 우선순위가 가장 높습니다. [권장 관련 튜토리얼: nodejs 동영상 튜토리얼process.nextTick()
和 Promise.resolve()
等函数。当涉及到执行 Node.js 中的异步代码时,微任务队列具有最高优先级。【相关教程推荐:nodejs视频教程、编程教学】
现在我们转到计时器队列。要将回调函数排入计时器队列,我们可以使用 setTimeout
和 setInterval
等函数。为了方便说明,本文将使用 setTimeout
。
为了理解计时器队列的执行顺序,我们会进行一系列实验,在微任务队列和计时器队列中入队任务。
// index.js setTimeout(() => console.log("this is setTimeout 1"), 0); setTimeout(() => console.log("this is setTimeout 2"), 0); setTimeout(() => console.log("this is setTimeout 3"), 0); 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"));
译注:大家不用紧张,这段代码就是在上篇“福利实验”的基础上,在开头加了 3 个
setTimeout
语句。
该代码包含三个 process.nextTick()
的调用,三个 Promise.resolve()
的调用和三个 setTimeout
的调用。每个回调函数记录适当的消息。所有三个 setTimeout
调用都有 0ms
的延迟,这表示在执行每个 setTimeout
语句时,回调函数都立即入队到计时器队列等待。第二次 process.nextTick()
和第二次 Promise.resolve()
都有一个额外的 process.nextTick()
语句,并且每个都带有一个回调函数。
当调用栈执行所有语句后,在 nextTick 队列中有 3 个回调,在 Promise 队列中有 3 个回调,在计时器队列中也有 3 个回调。没有代码要执行,控制权进入事件循环。
nextTick 队列具有最高优先级,其次是 Promise 队列,然后是计时器队列。从 nextTick 队列中获取第1 个回调并执行它,将一条消息记录到控制台。接着获取第 2 个回调并执行它,这也会记录一条消息。第 2 个回调包含 process.nextTick()
的调用,该方法将新的回调添加到了 nextTick 队列中。继续执行,并获取和执行第 3 个回调以及记录一条消息。最后,我们将新添加到 nextTick 队列的回调函数取出并在调用栈中执行,从而在控制台上输出了第四条日志信息。
当 nextTick 队列为空时,事件循环转向 Promise 队列。从队列中获取第 1 个回调,在控制台打印一条信息,第二个回调效果类似,并且还向 nextTick 队列添加了一个回调。 Promise 中的第 3 个回调被执行,接着日志消息被输出。此时 Promise 队列已空,事件循环检查 nextTick 队列是否存在新的回调,找到之后同样将消息记录到控制台。
现在,两个微任务队列都空了,事件循环转向计时器队列。我们有三个回调,每个回调依次从计时器队列中取出并在调用栈上执行,将分别打印 "setTimeout 1"、"setTimeout 2" 和 "setTimeout 3"。
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 this is setTimeout 1 this is setTimeout 2 this is setTimeout 3
微任务队列中的回调函数会在定时器队列中的回调函数之前执行。
到目前为止,优先顺序是 nextTick 队列,其次是 Promise 队列,然后是定时器队列。现在让我们继续进行下一个实验。
// index.js setTimeout(() => console.log("this is setTimeout 1"), 0); setTimeout(() => { console.log("this is setTimeout 2"); process.nextTick(() => console.log("this is inner nextTick inside setTimeout") ); }, 0); setTimeout(() => console.log("this is setTimeout 3"), 0); 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"));
第四个实验的代码大部分与第三个相同,只有一个例外。传递给第二个 setTimeout
函数的回调函数现在包含 process.nextTick()
, 프로그래밍 교육
setTimeout
및 setInterval
과 같은 함수를 사용할 수 있습니다. 설명의 편의를 위해 이 문서에서는 setTimeout
을 사용합니다. 타이머 큐의 실행 순서를 이해하기 위해 마이크로태스크 큐와 타이머 큐에 작업을 인큐하는 일련의 실험을 진행하겠습니다.
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 this is setTimeout 1 this is setTimeout 2 this is inner nextTick inside setTimeout this is setTimeout 3
🎜주석: 긴장하지 마세요. 이 단락 코드는 이전 "복지 실험"을 기반으로 하며 시작 부분에 세 개의 setTimeout
문이 추가되었습니다. 🎜
🎜이 코드에는 process.nextTick()
에 대한 세 번의 호출, Promise.resolve()
에 대한 세 번의 호출 및 setTimeout
세 번의 호출이 포함되어 있습니다. 부르다. 각 콜백 함수는 적절한 메시지를 기록합니다. 세 가지 setTimeout
호출 모두 0ms
지연이 있습니다. 즉, 각 setTimeout
문이 실행될 때 콜백 함수가 즉시 타이머에 추가됩니다. 서버 대기열이 기다리고 있습니다. 두 번째 process.nextTick()
과 두 번째 Promise.resolve()
에는 모두 추가 process.nextTick()
문이 있습니다. 콜백 함수로. 🎜process.nextTick()
에 대한 호출이 포함되어 있습니다. 실행을 계속하고 세 번째 콜백을 가져와 실행하고 메시지를 기록합니다. 마지막으로 nextTick 큐에 새로 추가된 콜백 함수를 꺼내 콜스택에서 실행시켜 콘솔에 네 번째 로그 메시지를 출력합니다. 🎜🎜nextTick 대기열이 비어 있으면 이벤트 루프가 Promise 대기열로 전환됩니다. 대기열에서 첫 번째 콜백을 가져와서 콘솔에 메시지를 인쇄합니다. 두 번째 콜백도 비슷한 효과를 가지며 nextTick 대기열에 콜백을 추가합니다. Promise의 세 번째 콜백이 실행되고 로그 메시지가 출력됩니다. 이때 Promise 대기열은 비어 있으며 이벤트 루프는 nextTick 대기열을 확인하여 새로운 콜백이 있는지 확인하고 나면 해당 메시지도 콘솔에 기록됩니다. 🎜🎜이제 두 개의 마이크로태스크 대기열이 모두 비어 있고 이벤트 루프가 타이머 대기열로 이동합니다. 세 개의 콜백이 있는데, 각각은 타이머 큐에서 가져와 콜 스택에서 실행되며 각각 "setTimeout 1", "setTimeout 2" 및 "setTimeout 3"을 인쇄합니다. 🎜// index.js setTimeout(() => console.log("this is setTimeout 1"), 1000); setTimeout(() => console.log("this is setTimeout 2"), 500); setTimeout(() => console.log("this is setTimeout 3"), 0);
🎜마이크로태스크 대기열의 콜백 함수는 타이머 대기열의 콜백 함수보다 먼저 실행됩니다. 🎜🎜지금까지 우선순위는 nextTick 대기열, Promise 대기열, 타이머 대기열 순입니다. 이제 다음 실험으로 넘어가겠습니다. 🎜
this is setTimeout 3 this is setTimeout 2 this is setTimeout 1🎜네 번째 실험의 코드는 한 가지를 제외하면 세 번째 실험과 대부분 동일합니다. 두 번째
setTimeout
함수에 전달된 콜백 함수에는 이제 process.nextTick()
에 대한 호출이 포함됩니다. 🎜🎜🎜시각화🎜🎜🎜🎜🎜让我们应用从之前的实验中学到的知识,快进到回调在微任务队列中已经被执行的点。假设我们有三个回调在计时器队列中排队等待。第一个回调出队并在调用堆栈上执行,“setTimeout 1”消息打印到控制台。事件循环继续运行第二个回调,“setTimeout 2”消息打印到控制台。同时,也会有一个回调函数入队了 nextTick 队列。
在执行计时器队列中的每个回调后,事件循环会返回检查微任务队列。检查 nextTick 队列确定需要执行的回调函数。这时第二个 setTimeout
推入的回调函数出队并在调用栈上执行,结果“inner nextTick”消息打印到控制台。
现在微任务队列为空了,控制权返回到计时器队列,最后一个回调被执行,控制台上显示消息“setTimeout 3”。
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 this is setTimeout 1 this is setTimeout 2 this is inner nextTick inside setTimeout this is setTimeout 3
微任务队列中的回调函数会在定时器队列中的回调函数执行之间被执行。
// index.js setTimeout(() => console.log("this is setTimeout 1"), 1000); setTimeout(() => console.log("this is setTimeout 2"), 500); setTimeout(() => console.log("this is setTimeout 3"), 0);
该代码包含三个 setTimeout
语句,包含三个不同的、入队时机不一样的回调函数。第一个 setTimeout
延迟 1000 毫秒,第二个延迟 500 毫秒,第三个延迟 0 毫秒。当执行这些回调函数时,它们只是简单地将一条消息记录到控制台中。
由于代码片段的执行非常简单,因此我们将跳过可视化实验。当多个 setTimeout
调用被发出时,事件循环首先排队最短延迟的一个并在其他之前执行。结果,我们观察到“setTimeout 3”先执行,然后是“setTimeout 2”,最后是“setTimeout 1”。
this is setTimeout 3 this is setTimeout 2 this is setTimeout 1
计时器队列回调按照先进先出(FIFO)的顺序执行。
实验表明,微任务队列中的回调比定时器队列中的回调具有更高优先级,并且微任务队列中的回调在定时器队列中的回调之间执行。定时器队列遵循先进先出(FIFO)顺序。
原文链接:Visualizing The Timer Queue in Node.js Event Loop,2023年4月4日,by Vishwas Gopinath
更多node相关知识,请访问:nodejs 教程!
위 내용은 이 문서에서는 노드 이벤트 루프의 타이머 큐를 이해하는 데 도움이 됩니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!