Node.js - 回调函数执行期间传入事件会发生什么?

4
假设我有一个带有一些重型同步处理的回调函数。在执行期间,事件循环不再自由轮询传入事件。那么这些事件会发生什么?它们会排队等待后续处理,还是会被丢弃?
谢谢。
3个回答

1

它们被添加到队列中,稍后进行处理:

JavaScript运行时包含一个消息队列,这是一系列要处理的消息列表。每个消息都与一个函数相关联。当堆栈为空时,从队列中取出一个消息并进行处理。处理包括调用关联的函数(从而创建初始堆栈帧)。当堆栈再次变为空时,消息处理结束。

并发模型和事件循环


该源码没有解释为什么运行时仍然可以排队事件。 - MicronXD
以下是事件循环的实现方式解释:事件循环的工作原理 - qzb

1

当事件被从事件队列中取出时,它们会按顺序排队并处理。

您的JS代码无法阻止新事件进入队列。


1
事件循环不会轮询,因此无法处理事件循环不会影响传入的事件。
事件循环的工作原理:
大多数现代操作系统(或类Unix古老操作系统)在操作系统级别而不是应用程序级别处理I/O。 POSIX标准要求操作系统至少支持select()系统调用。select()函数是大多数程序用于处理非阻塞I/O的阻塞函数。这个语句听起来有矛盾之处,但实际上并不是。
非阻塞I/O的工作原理:
我将使用select()作为例子,但不同的操作系统也有其他非阻塞API,如poll()和epoll()和重叠-I/O(Windows)。各种JavaScript引擎通常使用像libuv这样的库,在编译时自动处理要使用哪个API。
非阻塞API通常提供一个函数,比如select(),它会阻塞并等待应用程序正在侦听的任何I/O上的事件。为什么是阻塞的?因为这是程序使用0%CPU时间的唯一方法。否则,进程将忙于轮询,这将非常低效。
块式I/O是指任何告诉操作系统“嘿,我在等待这个‘事情’,所以你可以将我从CPU共享计划中移除,并且仅在‘事情’到来时唤醒我的任何功能。”非块式I/O和块式I/O的区别在于非块式I/O在等待多个I/O时会阻塞,而块式I/O在等待单个I/O时会阻塞。如果想了解更多,请查询“select()”POSIX函数的文档。无论如何,JavaScript使用非块式I/O,因此在读取I/O时不会阻塞,但在“select()”或类似的函数上会阻塞。当解释器执行JavaScript代码时,显然不会同时调用“select()”函数。因此,当解释器忙碌时,操作系统会缓冲程序的任何I/O。

操作系统轮询吗?

不。通常操作系统不会轮询(当然这取决于设备驱动程序,但一般来说不会)。I/O活动是由中断处理的。即使对于非中断驱动的I/O(例如USB),一般处理该I/O的芯片组在其缓冲区满时会生成一个中断,以便操作系统将数据复制到RAM中的OS缓冲区。有时候对于高速设备,甚至不是操作系统进行复制,而是DMA控制器,一旦数据被复制到RAM中就会生成一个中断。
关于GUI活动呢?
最终,GUI活动如鼠标点击和按键也是中断驱动的(早期基于DOS的GUI管理器如Windows 1.0使用轮询驱动的鼠标驱动程序,后来微软看了Mac OS的演示,据传说苹果的一名工程师泄露了他们不会轮询的事实,自那时起,鼠标驱动程序通常会触发中断)。
例外情况:
一个小的例外是JavaScript中的线程。所谓线程,指的是浏览器中的Web Workers和Node.js中的磁盘I/O处理程序。例如,在Node.js中,磁盘I/O驱动程序是作为单个线程中的阻塞I/O实现的。因此,Node.js负责在将数据传回事件循环之前缓冲数据。同样,所有的操作系统缓冲层仍然存在:例如,在复制数据时,操作系统可能会在Node.js线程调用下一个read()之前缓冲完成的磁盘读取命令。无论如何,这些线程仍然通过I/O通道与事件循环进行通信,可以是管道、套接字或Unix域套接字,因此我上面概述的一切仍然成立:如果主JS线程忙碌,操作系统将简单地从线程缓冲数据(或者如果它是阻塞的,则线程将简单地阻塞,直到事件循环处理它们的I/O)。

Adly又回来了,拿出最佳答案:P - MicronXD

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接