Node.js 执行程序需要哪些步骤? [英] What steps does Node.js takes to execute a program?

查看:68
本文介绍了Node.js 执行程序需要哪些步骤?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我探索 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.

具体来说,我想了解:

  1. 事件循环如何处理前两个阻塞 I/O 调用,以及;
  2. 关于异步.函数,一旦 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆