Node.js 中的非阻塞事件循环
NodeJS 具有同时做多件事的能力被称为异步编程。
想想在餐厅工作的服务员。他在餐厅周围接受顾客的订单,并在他们各自的食物准备好时为他们服务。当服务员接受顾客的订单并等到食物准备好、上菜然后继续给下一位顾客时会发生什么。这样一来,每位顾客的等待时间就会增加,这家餐厅将是一个巨大的失败。后者代表同步编程,前一个代表异步编程。
非阻塞: node.js 的非阻塞性质仅仅意味着 node.js 继续执行程序,而不是等待长时间的 I/O 操作或 HTTP 请求。即非JavaScript相关代码由不同线程或浏览器在后台处理,当node.js完成主程序执行并成功获取所需数据时执行。这样,长时间的操作不会阻塞程序剩余部分的执行。因此名称为非阻塞。
示例:查看以下代码片段,以更好地了解 Node.js 的非阻塞特性。
Javascript
console.log("First one to start");
setTimeout(() => {
console.log("I should wait for 3 seconds before execution");
}, 3000);
setTimeout(() => {
console.log("I should wait for 0 seconds before execution");
}, 0);
console.log("It's time for me to end");
输出:
如您所见,程序的其余部分不受长时间延迟的影响。但是为什么在“It's time for me to end”之后打印“I should wait for 0 seconds before execution”。好吧,让我们更深入地了解 Node.js 的内部工作。
调用堆栈:任何 Node.js函数在调用时都会进入调用堆栈。要调用的第一个函数是 main函数。当一个函数完成它的工作时,它会从调用堆栈中删除。
Node/Web API's:任何与 JavaScript 无关的函数,例如 HTTP 请求,都会被提供给使用 C++ 处理它的浏览器。处理完数据/请求后,浏览器会将其发送到回调队列。
CallBack 队列: Callback 队列保存准备好执行的异步函数(即完成了长时间的等待)。只有当调用堆栈变空时,才会调用回调队列中存在的项目。
事件循环:一旦调用堆栈为空,事件循环就会继续检查回调队列以执行异步函数。
代码是如何执行的:
步骤 1:将 main函数推送到调用堆栈。
第 2 步:第一个 console.log 语句被推送到调用堆栈。
第 3 步:打印“First one to start”,并从调用堆栈中删除 console.log函数。
第四步:将setTimeOut(3000)函数交给浏览器处理。
第五步:将setTimeOut(0)函数交给浏览器处理。
第 6 步: setTimeOut(0)函数已被处理并提供给回调队列。
第 7 步:最后一个 console.log 语句被推送到调用堆栈。
第 8 步:打印“It's time for me to end”,并从调用堆栈中删除 console.log函数。
第 9 步:将 main函数从调用堆栈中移除,事件循环开始运行。
第 10 步: setTimeOut(0) 中的 console.log函数被推送到调用堆栈。
第 11 步:打印“我应该在执行前等待 0 秒”,并将 console.log函数从调用堆栈中删除。
第12步:等待3秒后,浏览器将setTimeOut(3000)函数交给CallBack Queue。
第 13 步: setTimeOut(3000) 中的 console.log函数被推送到调用堆栈。
第 14 步:打印“I should wait for 3 seconds before execution”,并从调用堆栈中删除 console.log函数。
图表: