1:如果我们谈论的是单线程应用程序,那么当JS引擎接受更多请求并执行它们时,什么处理setTimeouts?那单线程不是会继续处理其他请求吗?那么当其他请求不断到来并被执行时,谁将继续处理 setTimeout 。
节点进程中只有 1 个线程实际执行程序的 JavaScript。然而,在节点本身内部,实际上有几个线程处理事件循环机制的操作,其中包括一个 IO 线程池和一些其他线程。关键是这些线程的数量与正在处理的并发连接的数量并不对应,就像在每个连接的线程并发模型中一样。
现在关于“执行 setTimeouts”,当您调用setTimeout
,所有节点所做的基本上就是更新将来某个时间要执行的函数的数据结构。它基本上有一堆需要做的事情的队列,事件循环的每个“滴答声”都会选择一个,将其从队列中删除,然后运行它。
需要理解的关键一点是,节点依赖操作系统来完成大部分繁重的工作。因此,传入的网络请求实际上是由操作系统本身跟踪的,当节点准备好处理请求时,它只使用系统调用向操作系统请求网络请求,并准备好处理数据。 IO“工作”节点所做的大部分工作都是“嘿操作系统,有一个网络连接,数据已准备好读取?”或“嘿操作系统,我的任何未完成的文件系统调用都已准备好数据吗?”。基于其内部算法和事件循环引擎设计,节点将选择 JavaScript 的一个“tick”来执行、运行它,然后再次重复该过程。这就是事件循环的含义。 Node 基本上始终在确定“我应该运行的下一段 JavaScript 是什么?”,然后运行它。这会影响操作系统已完成的 IO,以及通过调用在 JavaScript 中排队的内容setTimeout
or process.nextTick
.
2:如果这些setTimeout将在幕后执行,同时更多的请求传入并执行,那么幕后执行异步执行的就是我们正在谈论的EventLoop?
JavaScript 不会在幕后执行。程序中的所有 JavaScript 都在前端和中心运行,一次运行一个。幕后发生的事情是操作系统处理 IO,节点等待其准备就绪,节点管理其等待执行的 javascript 队列。
3:JS引擎如何知道它是否是一个异步函数,以便将其放入EventLoop中?
节点核心中有一组固定的异步函数,因为它们进行系统调用,并且节点知道这些函数是哪些,因为它们必须调用操作系统或 C++。基本上,所有网络和文件系统 IO 以及子进程交互都将是异步的,JavaScript 使 Node 异步运行某些内容的唯一方法是调用 Node 核心库提供的异步函数之一。即使您使用定义了自己的 API 的 npm 包,为了产生事件循环,最终该 npm 包的代码将调用节点核心的异步函数之一,此时节点知道刻度已完成并且可以启动事件再次循环算法。
4 事件循环是一个回调函数队列。当异步函数执行时,回调函数被推入队列。在执行异步函数之后的代码之前,JavaScript 引擎不会开始处理事件循环。
是的,这是事实,但它具有误导性。关键是正常模式是:
//Let's say this code is running in tick 1
fs.readFile("/home/barney/colors.txt", function (error, data) {
//The code inside this callback function will absolutely NOT run in tick 1
//It will run in some tick >= 2
});
//This code will absolutely also run in tick 1
//HOWEVER, typically there's not much else to do here,
//so at some point soon after queueing up some async IO, this tick
//will have nothing useful to do so it will just end because the IO result
//is necessary before anything useful can be done
所以,是的,您可以通过在同一时间同步计算内存中的斐波那契数来完全阻止事件循环,是的,这会完全冻结您的程序。这是合作并发。 JavaScript 的每次更新都必须在合理的时间内产生事件循环,否则整个架构就会失败。