Node.js在非阻塞函数返回后的回调处理

Node.js callback handling after nonblocking function returns

本文关键字:返回 回调 处理 函数 js Node      更新时间:2023-11-15

我正在读一本Node.js的书,试图清楚地了解Node.js如何处理事件、异步函数及其回调、非阻塞I/O等。下面是我如何理解事件循环基础知识的简要简介,然后是我在书中找不到明确答案的几个问题。如果我的基本理解有什么错误,请纠正我。然后提问。

据我所知,Node.js在一个线程上运行所有功能。这包括事件循环。换句话说,当事件发生时,例如HTTP请求,该请求将进入事件队列。事件循环本身一次从队列中拉出一个事件,并对其进行整体处理。这可能意味着立即返回一个简单的结果,或者在向第三方(如数据库或文件系统)进行I/O的情况下,进行一个非阻塞的异步调用,并通过回调告诉系统在异步函数完成时该做什么。同时,队列中的所有其他事件都在等待。如果开发人员编写的方法不异步或不向工作人员卸载工作,那么在处理任务时,整个服务器将挂起。

如果其中任何一个是错误的,请澄清,因为这可能会影响我对以下问题答案的理解,我希望这些问题不要太愚蠢。

  1. 文件系统、数据库和其他类似的调用不会被阻塞,因为Node.js将它们卸载到异步调用,但如果Node.js是单线程的,谁在监听新事件并管理事件循环?当Node.js正在处理一些事件时,谁在监听和管理新传入事件的队列,这样当Node.jss的注意力在别处时,它们就不会丢失?我是否从根本上误解了它的工作原理?除了运行我们代码的引擎部分之外,是否有类似于运行守护程序的东西来管理事件队列
  2. 异步函数回调的类似问题。。。当Node.js已经在处理一些回调或事件时,谁在监听和管理传入回调,这样回调就不会因为处理过程中的到来而丢失
  3. 当异步函数完成时,回调是否会中断事件循环并立即控制,或者异步函数的返回是否会作为队列末尾的新事件重新进入事件循环并再次等待轮到它自己?换句话说,异步函数的回调是像对待其他事件一样对待,还是不同对待?如果区别对待,怎么办?这是一个根本愚蠢的问题吗
  4. 回调是否会像写得不好的事件处理程序(例如HTTP请求处理程序)一样被阻塞?换句话说,回调中包含的写得不好的代码会阻塞服务器吗?(我想答案是肯定的,但我想知道我是否是对的。)

这是一个类似的问题,答案很好,但细微之处略有不同。这个链接的问题解决了用户代码的异步性,但其中一个答案似乎包含了我问题的附带答案。我对Node的内部工作很感兴趣,用户从未与之交互,例如,在Node阻塞时,谁管理新的客户端请求插入事件队列,回调也是如此,等等。NodeJS真的是单线程的吗?

首先,从以下内容开始会有所帮助:

"Node.js是单线程的"这句话有点误导,在技术上也不正确。对于代码中的所有意图和目的,您的代码是单线程的,但Node本身不是。Node中有一些API和操作(通常是异步I/O)将在内核或LibUV的线程池中使用不同的线程。

现在按照每个问题的顺序进行分解:

  1. 文件系统、数据库和其他类似的调用不会被阻塞,因为Node.js将它们卸载到异步调用,但如果Node.js是单线程的,谁在监听新事件并管理事件循环?当Node.js正在处理一些事件时,谁在监听和管理新传入事件的队列,这样当Node.jss的注意力在别处时,它们就不会丢失?我是否从根本上误解了它的工作原理?除了运行我们代码的引擎部分之外,是否有类似于运行守护程序的东西来管理事件队列

当Node正在处理某个事件时,没有人在侦听队列。它们不会丢失,因为这是一个队列。它会等到Node完成它正在做的事情,这样它就可以返回队列检查下一件事。这是Node"事件循环"的一部分,也是为什么阻塞代码(从不允许Node返回事件循环顶部或需要很长时间才能返回的代码)有问题。

异步I/O操作在单独的线程中,可以在Node执行其他操作时将东西存放在队列中。当Node完成了这件事时,它可以处理队列中的事情。

  1. 异步函数回调的类似问题。。。当Node.js已经在处理一些回调或事件时,谁在监听和管理传入回调,这样回调就不会因为处理过程中的到来而丢失

同样,它没有丢失,因为它是队列。它是数据结构的一个基本部分。事件将在队列中等待,直到有东西将其从队列中删除。无论当时是否有任何东西在听,它都不会消失,它必须被消耗掉。

  1. 当异步函数完成时,回调是否会中断事件循环并立即控制,或者异步函数的返回是否会作为队列末尾的新事件重新进入事件循环并再次等待轮到它自己?换句话说,异步函数的回调是像对待其他事件一样对待,还是不同对待?如果区别对待,怎么办?这是一个根本愚蠢的问题吗

异步函数的回调将进入队列,没有任何方法可以"中断"事件循环的当前流。事件循环确实有多个不同的顺序,所以它可能不会像异步IO事件那样被处理(我记不清了),但回调仍然需要等待。

  1. 回调是否会像写得不好的事件处理程序(例如HTTP请求处理程序)一样被阻塞?换句话说,回调中包含的写得不好的代码会阻塞服务器吗?(我想答案是肯定的,但我想知道我是否是对的。)

是的,回调只是一个函数调用,函数调用不一定是异步的。你可以写这样的代码来阻止:

function add(a, b, cb) { cb(a + b) }
async.whilst(
  function() { return true },
  function(cb) { add(1 + 1, cb) },
  function() { console.log('Done!') }
)

虽然从技术上讲,这段代码使用回调,但它不执行任何异步操作,因此Node将继续评估函数调用,而从不检查事件循环。也就是说,您很少看到这种情况,因为回调通常涉及异步操作,或者它们不是为了使用回调而编写的。

希望这能有所帮助,如果有什么不清楚的地方,请告诉我!

随着时间的推移,我从各种来源重新收集。

Node.js并不完全是单线程的。它将Disk、DB、HTTP API调用卸载到其他线程,这些线程一旦在事件循环中将它们的回调排入队列。

  1. 还涉及其他线程
  2. 事件循环应该是一个类似队列的数据结构,可由多个线程访问
  3. 大多数情况下重新进入事件循环(在队列末尾)。但是有一些方法可以把它放在前面。(需要检查)
  4. 是的。例如,如果您使用fs.readFileSync,它会阻塞服务器一段时间

事件循环:http://2014.jsconf.eu/speakers/philip-roberts-what-the-heck-is-the-event-loop-anyway.html

我是凭记忆写的。如果这对你有帮助,我可以试着查找参考资料。