NodeJS 中的非阻塞事件循环
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");
输出:
如您所见,程序的其余部分不受长时间延迟的影响。但是为什么“我应该在执行前等待 0 秒”打印在“我该结束了”之后。好吧,让我们更深入地了解 Node.js 的内部工作。
调用堆栈:任何 Node.js函数在调用时都会进入调用堆栈。要调用的第一个函数是 main函数。当一个函数完成它的工作时,它会从调用堆栈中删除。
Node/Web API's :任何与 JavaScript 无关的函数,如 HTTP 请求,都被提供给使用 C++ 处理它的浏览器。处理完数据/请求后,浏览器将其发送到回调队列。
回调队列:回调队列保存准备执行的异步函数(即漫长的等待期已完成)。只有当调用堆栈变空时才会调用回调队列中存在的项目。
事件循环:一旦调用堆栈为空,事件循环就会不断检查回调队列以执行异步函数。
代码是如何执行的:
第一步:将main函数压入调用栈。
第二步:第一条console.log语句被推送到调用栈。
第 3 步:打印“First one to start”并从调用堆栈中删除 console.log函数。
第四步:将setTimeOut(3000)函数交给浏览器处理。
第五步:将setTimeOut(0)函数交给浏览器处理。
第 6 步: setTimeOut(0)函数已被处理并提供给回调队列。
第七步:最后一个console.log语句被推送到调用栈。
第 8 步:打印“It's time for me to end”并从调用堆栈中删除 console.log函数。
第九步:主函数从调用栈中移除,事件循环开始运行。
第 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函数。
图表: