Node.js执行程序有哪些步骤?

我正在探索node.js很长一段时间了。 但是还没有弄清楚,程序是如何执行的。

例如采取这个简单的程序:

// 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 (在命令行上)时,幕后会发生什么,直到程序结束。

具体来说,我想了解:

  1. 事件循环如何处理前两个阻塞的I / O调用,
  2. 视为asynchronous。 函数,一旦I / O完成,它发送到事件队列以及附加callback被调用的方式。

阻塞I / O就是这样:呼叫在它完成之前不会返回。 所以当你调用readFileSync ,它会直接调用OS API来读取文件,并且只有当整个内容被复制到一个Buffer才会把控制权返回给你的JS。

通过阻塞(同步)函数,事件循环与处理I / O的方式完全无关。 这就是为什么同步I / O在节点上非常糟糕的原因 – 它防止了事件循环没有运行的事情发生。

另一方面,像readFile这样的正常(asynchronous)I / O函数只需要完成一个I / O请求,然后返回。 到底I / O的执行方式取决于I / O请求的性质(即文件系统还是networking),但它是由单独的线程上的C库libuv处理的。

  • networkingI / O实际上是使用主机操作系统的本地asynchronous I / O 设施执行的 ,这非常快速,并且不需要为每个连接分别设置一个线程。

    每当操作系统报告networkingI / O活动时,事件就被放入一个队列中。 在事件循环的下一个勾号处,事件被拾取并分派给相应的JavaScript函数。 (C代码可以获得对JS函数的引用并调用它们)

  • 文件I / O使用普通的阻塞系统I / O调用来完成。 每个I / O请求都放在libuv工作队列中 ,并由线程池执行。 这里的关键是阻塞I / O在C中完成,在与JavaScript线程分离的线程上。

    当阻塞I / O调用返回时,结果放入队列中。 再一次,事件在下一个打勾处发出。

节点保留待处理工作请求的引用计数,例如I / O,定时器和侦听服务器。 如果在勾号的末尾有零,则该过程退出。 (在某些情况下,可以使用unref()从引用计数中显式删除活动请求。)

其他一些相关的答案将有助于进一步解释这些概念:

  • 什么是一个node.js事件循环打勾?
  • 进一步讨论节点中的I / O

最后,让我们来看看你的示例程序。 这正是发生的事情:

  • require('fs')给你一个已经初始化的fs模块的引用。 内置模块是特殊的,不需要I / O来加载。
  • fs.readFileSync调用OS文件API。 直到文件内容被复制到内存中才会返回。
  • fs.readFile将一个项目(包含文件名和对callback的引用)添加到libuv工作队列中,然后立即返回。 (因为工作队列上没有任何东西,I / O很快就会在一个单独的线程上开始。)
  • fileBuffer的内容被logging到控制台。
  • 在这一点上,控制到达你的程序结束,所以它返回到节点。 这是第一次打勾的结束。 由于有待处理的I / O,节点不会退出。
  • 节点进入“空闲”状态。 事件循环以无限循环的方式检查事件队列中的新项目。 许多蜱传经过,直到…
  • asynchronousI / O操作最终完成并将结果添加到事件队列中。 下一个滴答声将事件从队列中popup,并调用最初传递给readFile的callback函数。
  • data的内容被logging到控制台,callback函数返回。
  • 滴答已经结束,并没有更多的待定工作。 节点退出。
  1. 节点是单线程的。 阻塞调用正在执行时,什么都不会发生。 没有事件会触发,不会执行callback。 这就是为什么阻止呼叫总是在节点世界非常沮丧的原因。 ReadFileSync应该用于命令行脚本,或者用于引导您的应用程序。
  2. 不太清楚你到底在问什么。 在你列举的例子中,callback接收(这是很常见的)两个参数。 一个是null或一个Error实例,另一个是数据。 这个方法接收一个Buffer作为第二个参数。 将缓冲区转换为string的简单方法是data.toString() ,它将字节列表转换为UTF-8编码的string。

您会注意到,几乎每个Node模块都使用此callback签名; 一个可选的Error ,然后是数据。

从命令行运行时,您会注意到应用程序在最后一次IO操作完成时退出。 节点自己跟踪这个。 在构buildWeb应用程序时,开放的HTTP(S)连接将保持进程“繁忙”。