Node.js 执行程序需要哪些步骤? [英] What steps does Node.js takes to execute a program?
问题描述
我探索 node.js 已经有一段时间了.但是还没有确切地弄清楚程序是如何执行的.
I'm exploring node.js for quite some time now. But yet haven't figured out exactly, how a program executes.
例如采取这个简单的程序:
For e.g. taking this simple program:
// file app.js`
var fs = require('fs');
var fileBuffer = fs.readFileSync('./sample.txt');
fs.readFile('./resource.json', function (er, data) {
console.log(data);
});
console.log(fileBuffer);
现在,当我们输入 node app.js
(在命令行上)时,幕后会发生什么直到程序结束.
Now, when we type in node app.js
(on the command line), what goes on behind the scenes until the program ends.
具体来说,我想了解:
- 事件循环如何处理前两个阻塞 I/O 调用,以及;
- 关于异步.函数,一旦 I/O 完成,它向事件队列发送什么以及如何&当附加的回调被调用时.
推荐答案
阻塞 I/O 就是这样:调用在完成之前不会返回.因此,当您调用 readFileSync
时,它会直接调用 OS API 以读取文件,并且只有当整个内容都已复制到 Buffer
中时,才会将控制权返回给您JS.
Blocking I/O is just that: the call does not return until it has completed. So when you call readFileSync
, it makes calls directly to the OS API to read in a file and only when the entire contents have been copied into a Buffer
does control return to your JS.
对于阻塞(同步)函数,事件循环与 I/O 的处理方式完全无关.这就是为什么节点中的同步 I/O 非常糟糕的原因由于事件循环没有运行,它可以防止其他任何事情发生.
With blocking (synchronous) functions, the event loop has absolutely nothing to do with how the I/O is handled. This is why synchronous I/O is very bad in node – it prevents anything else from happening since the event loop is not running.
另一方面,像 readFile
这样的普通(异步)I/O 函数只是简单地提出 I/O 的请求然后返回.I/O 的具体执行方式取决于 I/O 请求的性质(即是文件系统还是网络),但都由 C 库处理 libuv 在一个单独的线程上.
On the other hand, the normal (asynchronous) I/O functions like readFile
simply put in a request for I/O to be done and then return. Exactly how that I/O is performed depends on the nature of the I/O request (ie whether filesystem or network), but it is all handled by the C library libuv on a separate thread.
网络 I/O 实际上是使用主机操作系统的原生 异步 I/O facilities,它非常快速并且不需要为每个连接单独的线程.
Network I/O is actually performed using the host OS's native asynchronous I/O facilities, which is very fast and does not necessitate a separate thread for each connection.
每当操作系统报告网络 I/O 活动时,该事件就会被放入队列中.在事件循环的下一个滴答声中,事件被拾取并分派到适当的 JavaScript 函数.(C 代码可以获得对 JS 函数的引用并调用它们.)
Whenever the OS reports network I/O activity, the event is placed in a queue. On the next tick of the event loop, the event is picked up and dispatched to the appropriate JavaScript function. (C code can obtain a reference to JS functions and invoke them.)
文件 I/O 是使用正常的阻塞系统 I/O 调用完成的.每个 I/O 请求都放在 libuv 工作队列并由线程池执行.这里的关键是阻塞 I/O 是在 C 中完成的,在一个独立于 JavaScript 线程的线程上.
File I/O is done using normal blocking system I/O calls. Each I/O request is placed in the libuv work queue and executed by a thread pool. The key here is that the blocking I/O is done in C, on a thread separate from the JavaScript thread.
当阻塞 I/O 调用返回时,结果被放入队列中.同样,事件在下一个滴答声中被调度.
When the blocking I/O call returns, the result is placed in a queue. Again, the event is dispatched on the next tick.
节点保持未决工作请求的引用计数–诸如 I/O、计时器和侦听服务器之类的东西.如果滴答结束时为零,则该过程退出.(在某些情况下,您可以使用 unref()
.)
Node keeps a reference count of pending work requests – things like I/O, timers, and listening servers. If there are zero at the end of a tick, the process exits. (In some cases, you can explicitly remove an active request from the reference count with unref()
.)
其他一些相关答案将有助于进一步解释其中一些概念:
Some other related answers will help explain some of these concepts further:
最后,让我们来看看您的示例程序.这正是发生的事情:
Finally, let's walk through your example program. Here's exactly what happens:
require('fs')
提供对已经初始化的 fs 模块的引用.内置模块很特殊,不需要加载 I/O.fs.readFileSync
调用 OS 文件 API.直到将文件内容复制到内存中才会返回.fs.readFile
向 libuv 工作队列添加一个项目(包含文件名和对回调的引用),然后立即返回.(因为工作队列中不会有任何内容,I/O 很快就会在一个单独的线程上开始.)fileBuffer
的内容被记录到控制台.- 此时,控制权到达程序的末尾,因此返回到节点.这是第一个刻度的结束.由于有待处理的 I/O,节点不会退出.
- 节点进入空闲"状态.事件循环在无限循环中检查事件队列中的新项目.许多滴答声经过,直到...
- 异步 I/O 操作最终完成并将结果添加到事件队列中.下一个滴答声将事件从队列中弹出并调用您最初传递给
readFile
的回调函数. data
的内容被记录到控制台,回调函数返回.- tick 已结束,没有更多待处理的工作.节点退出.
require('fs')
gives you a reference to the already-initialized fs module. The builtin modules are special and do not require I/O to load.fs.readFileSync
calls in to the OS file APIs. It does not return until the file contents are copied into memory.fs.readFile
adds an item (containing the filename and a reference to the callback) to the libuv work queue, then returns immediately. (Because there would be nothing on the work queue, the I/O will begin shortly on a separate thread.)- The contents of
fileBuffer
are logged to the console. - At this point, control reaches the end of your program, so it goes back to node. This is the end of the very first tick. Since there is pending I/O, node does not exit.
- node enters an "idle" state. The event loop checks the event queue for new items in an indefinite loop. Many ticks pass by until...
- The asynchronous I/O operation finally completes and adds the result to the event queue. The next tick pops the event off the queue and invokes the callback function you initially passed to
readFile
. - The contents of
data
are logged to the console and the callback function returns. - The tick has ended, and there is no more pending work. Node exits.
这篇关于Node.js 执行程序需要哪些步骤?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!