前言
如果你了解过Node.js
,那么你一定听说过事件循环。你一定想知道它为什么那么特殊,并且为什么你需要关注它?此时此刻的你,可能已经写过许多基于Express.js
的后端代码,但没有接触到任何的循环。
在下文中,我们会先在一个更高的,无关操作系统的层面上了解事件循环,然后再去深入到Node.js
中观察它。
事件和事件处理器
在事件循环里,有两个主要角色:
事件
事件处理器,即这些事件的订阅者
事件,可以是十分底层的操作系统事件,如“文件已经准备好被写入”或“收到了一个新的HTTP请求”。
事件处理器,则是当指定事件触发时,执行的一段代码。
事件循环中,事件的获取和事件处理器的执行
事件循环的职责,就是不断得等待事件的发生,然后将这个事件的所有处理器,以它们订阅这个事件的时间顺序,依次执行。当这个事件的所有处理器都被执行完毕之后,事件循环就会开始继续等待下一个事件的触发,不断往复。
当同时并发地处理多个请求时,以上的概念也是正确的,可以这样理解:在单个的线程中,事件处理器是一个一个按顺序执行的。
即如果某个事件绑定了两个处理器,那么第二个处理器会在第一个处理器执行完毕后,才开始执行。在这个事件的所有处理器都执行完毕之前,事件循环不会去检查是否有新的事件触发。在单个线程中,一切都是有顺序地一个一个地执行的!
在事件处理器的执行代码中触发了事件
一个有趣而且常会出现的情况是,在执行一个事件处理器的代码里,代码触发了另一个事件。例如,在文件可以被读之后,这个事件的处理器开始读取内容,期间处理器又触发了一个写事件,来将这个文件中已读取的这部分内容响应给正在处理的HTTP请求。写入完毕之后,继续读取文件。这就是事件循环保持运作的方式。
事件被触发,然后以订阅顺序执行处理器,不断往复。这个循环圈就是事件循环控制流的关键 ,在没有更多的订阅事件的处理器之后,Node.js
就会退出。
操作系统的帮助
事件在实质上是从哪里来?事件循环会不断获取下一个被触发的事件,这是如何发生的?你是对的,这需要操作系统的帮助。幸运的是,现代操作系统中有许多方式可以实现这些(select
,epoll
,kqueue
,IOCP
)。在日常使用时,通常会在操作系统提供的这些方式上会再抽象出一层(在Node.js
中,就是libuv
)。
另一个需要操作系统帮助的,就是事件的订阅,如注册在特定的事件发生时需要执行的代码。这也是事件循环中必须要实现的。
Node.js
中的事件循环
事件循环是Node.js
中非常核心的组成部分,许多Node.js
的特性都依赖于它,它既有积极的影响也不好的影响。比如在处理I/O密集任务时的性能提升和缺乏足够信息量的错误堆栈信息。Node.js
异步回调驱动的编程范式,便直接是源于事件循环的存在。
每一个Node.js
进程中都存在一个事件循环。只要进程存在,它就存在,一直不间断地调度执行着你程序中的方法和操作系统方法。事件循环以一个无限循环的形式启动,存在于Node.js
二进制文件里main
函数的最后,当没有更多可被执行的事件处理器时,它就退出。它运行于单个线程中,并且事件处理器是一个接一个顺序执行的。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。