Fla*_*dun 17 javascript event-loop node.js nodejs-server
在这个问题的回答中——
Node.js 中的非阻塞或异步 I/O 是什么?
这个描述听起来与普通 js 中的事件循环没有什么不同。两者有区别吗?如果不是,事件循环是否只是简单地重新命名为“异步非阻塞 I/O”,以便更容易地销售 Node.js 而不是其他选项?
use*_*170 11
事件循环就是这种机制。异步 I/O 是目标。
\n异步 I/O 是一种编程风格,其中 I/O 调用在返回之前不等待操作完成,而只是安排在发生这种情况时通知调用者,并将结果返回到某处。在 JavaScript 中,通知通常通过调用回调或解析 Promise 来执行。对于程序员来说,这如何发生并不重要:它就是这样。我请求该操作,当它\xe2\x80\x99 完成时,我会收到通知。
\n事件循环是通常实现这一点的方式。问题是,在大多数 JavaScript 实现中,实际上在某处存在一个循环,最终归结为:
\nwhile (poll_event(&ev)) {\n dispatch_event(&ev);\n}\nRun Code Online (Sandbox Code Playgroud)\n然后,通过安排操作的完成作为事件由该循环接收,并将其分派到调用者\xe2\x80\x99s 选择的回调来完成异步操作。
\n有多种方法可以实现不基于事件循环的异步编程,例如使用线程和条件变量。但历史原因使得这种编程风格在 JavaScript 中实现起来相当困难。因此,在实践中,JavaScript 中异步的主要实现是基于从全局事件循环中调度回调。
\n换句话说,\xe2\x80\x98事件循环\xe2\x80\x99描述了主机做什么,而\xe2\x80\x98异步I/O\xe2\x80\x99描述了程序员做什么。
\n从非程序员\xe2\x80\x99s鸟\xe2\x80\x99s的角度来看,这可能看起来像是吹毛求疵,但这种区别有时可能很重要。
\n有 2 种不同的事件循环:
事件循环是一个持续运行的进程,执行排队的任何任务。它有多个任务源,保证该源内的执行顺序,但浏览器可以在循环的每一轮中选择从哪个源获取任务。这允许浏览器优先处理性能敏感的任务,例如用户输入。
浏览器事件循环会持续检查几个不同的步骤:
任务队列- 可以有多个任务队列。浏览器可以按照它们喜欢的任何顺序执行队列。同一队列中的任务必须按照到达的顺序执行,先进先出。任务按顺序执行,浏览器可以在任务之间进行渲染。来自同一源的任务必须进入同一队列。重要的是任务将从头到尾运行。每个任务完成后,事件循环将进入微任务队列并从那里执行所有任务。
微任务队列- 微任务队列在每个任务结束时进行处理。微任务期间排队的任何其他微任务都会添加到队列末尾并进行处理。
动画回调队列- 动画回调队列在像素重绘之前处理。队列中的所有动画任务都将被处理,但在动画任务期间排队的任何其他动画任务将被安排到下一帧。
渲染管道- 在此步骤中,将进行渲染。浏览器可以决定何时执行此操作,并且它会尝试尽可能高效。仅当确实存在值得更新的内容时,才会执行渲染步骤。大多数屏幕以设定频率更新,大多数情况下每秒 60 次 (60Hz)。因此,如果我们每秒更改页面样式 1000 次,则渲染步骤不会每秒处理 1000 次,而是会与显示同步,并且仅渲染至显示能够执行的频率。
值得一提的是 Web API,它们实际上是线程。例如,setTimeout()浏览器向我们提供了一个 API。当你调用setTimeout()Web API时,它会接管并处理它,并将结果作为任务队列中的新任务返回到主线程。
我发现描述事件循环如何工作的最好的视频是这个。当我研究事件循环如何工作时,它对我帮助很大。另一个很棒的视频是这个和这个。您绝对应该检查所有这些。
NodeJS 事件循环允许 NodeJS 通过尽可能将操作卸载到系统内核来执行非阻塞操作。大多数现代内核都是多线程的,它们可以在后台执行多个操作。当这些操作之一完成时,内核会通知 NodeJS。
为 NodeJS 提供事件循环的库称为 Libuv。默认情况下,它将创建一个名为“线程池”的东西,其中有 4 个线程来卸载异步工作。如果需要,您还可以更改线程池中的线程数。
NodeJS 事件循环经历不同的阶段:
计时器setTimeout()- 此阶段执行由和安排的回调setInterval()。
挂起的回调- 执行推迟到下一个循环迭代的 I/O 回调。
空闲,准备-仅在内部使用。
poll - 检索新的 I/O 事件;执行 I/O 相关的回调(几乎所有回调,关闭回调除外,由计时器调度的回调),setImmediate()Node 将在适当的时候阻塞在这里。
check -setImmediate()此处调用回调。
关闭回调- 一些关闭回调,例如socket.on('close', ...).
在每次运行事件循环之间,Node.js 都会检查是否正在等待任何异步 I/O 或计时器,如果没有,则彻底关闭。
在浏览器中,我们有 Web API。在 NodeJS 中,我们有具有相同规则的 C++ API。
如果您想了解更多信息,我发现此视频非常有用。