2

根据评论/答案,请参阅问题底部的更新:这个问题实际上是关于不执行回调的隐藏线程的可能性。


我有一个关于节点请求模块的潜在神秘场景的问题,其中:

  • 一个完整的 HTTP 请求在网络上构建和执行(需要很多毫秒甚至几秒)

  • ...在本地机器上运行时执行单个函数之前(通常以纳秒为单位?) - 请参阅下文了解详细信息

我发布此内容主要是为了确保我不会误解有关 Node / JS / Request 模块代码的内容。

请求模块中的示例(请参阅该部分中的第二个示例),是这样的:

// Copied-and-pasted from the second example in the 
// Node Request library documentation, here:
// https://www.npmjs.com/package/request#examples

// ... My ARCANE SCENARIO is injected in the middle

var request = require('request')
  request(
    { method: 'GET'
    , uri: 'http://www.google.com'
    , gzip: true
    }
  , function (error, response, body) {
      // body is the decompressed response body 
      console.log('server encoded the data as: ' + (response.headers['content-encoding'] || 'identity'))
      console.log('the decoded data is: ' + body)
    }
  )

    // **************************************************** //
    // Is the following scenario possible?
    //
    // <-- HANG HANG HANG HANG HANG HANG HANG HANG HANG -->
    //
    // Let us pretend that the current thread HANGS here,
    // but that the request had time to be sent,
    // and the response is pending being received by the thread
    //
    // <-- HANG HANG HANG HANG HANG HANG HANG HANG HANG -->
    // **************************************************** //

.on('data', function(data) {
    // decompressed data as it is received 
    console.log('decoded chunk: ' + data)
  })
  .on('response', function(response) {
    // unmodified http.IncomingMessage object 
    response.on('data', function(data) {
      // compressed data as it is received 
      console.log('received ' + data.length + ' bytes of compressed data')
    })
  })

我在代码片段中指出了我的神秘场景。

假设 Node 进程在指示点挂起,但 Node 内部(在隐藏线程中,对 Javascript 不可见,因此不调用任何回调)能够构造请求,并通过网络发送;假设挂起一直持续到收到响应(比如说,分成两个块)并等待节点处理。(这种情况肯定是神秘的,我什至不确定理论上是否可行。)

然后假设挂起结束,上面的Node线程被唤醒。此外,假设(以某种方式)Node 能够一直处理响应,直到执行上述代码中的回调函数(但又没有移过原始代码路径中代码中的“挂起”点) ,如果这在理论上是可能的)。

上述神秘的场景在理论上是可能的吗?'data'如果是这样,在对象上安排事件之前,是否会通过网络接收数据包并进行组合,准备好传递给回调函数?在这种情况下,如果可能的话,我想这个'data'事件会被错过。

同样,我知道这是一个神秘的场景——考虑到所涉及的内部机制和编码,这在理论上可能是不可能的。

那是我的问题 - 上述神秘的场景,其极不可能的比赛条件,但理论上是可能的吗?

我问只是为了确保我没有遗漏一些关键点。谢谢。


更新:来自评论和答案:我现在已经澄清了我的问题。“神秘场景”需要有一个隐藏线程(因此不能执行任何用户代码,包括回调)来构造请求,通过网络发送它并接收响应 - 没有任何回调触发,包括'data'回调 - 并在回调准备好被调用时停止'response',等待(单个)可见 JS 线程唤醒。

4

3 回答 3

3

不,这不可能发生。

是的,确实有“隐藏”的后台线程为异步方法工作,但那些不调用回调。javascript 的所有执行确实发生在同一个线程上,同步,顺序。该data事件回调将始终异步执行,即在当前脚本/函数运行完成之后。

虽然在创建回调并将其附加到事件发射器之前确实可能已经从网络到达数据包,但始终在发送请求之前创建侦听最低级别数据包的回调 - 它是本机“makeRequest”的参数" 方法,并且可以从一开始就被调用。因此,当数据包在当前脚本(仍被构造事件发射器和附加处理程序占用)完成之前到达时,此事件将排队,并且只有在事件循环准备好后才会执行回调 - 在下一回合。到那时,data肯定会创建并附加事件回调。

于 2015-06-18T04:45:05.760 回答
2

nodejs Javsacript 执行是单线程和事件驱动的。这意味着一切都通过事件队列运行。Javascript 执行线程一直运行,直到完成,然后系统检查事件队列以查看是否还有其他事情要做(等待触发的计时器、等待调用的异步回调等)。

nodejs 在其某些实现中确实使用了一些内部线程(例如文件 I/O),但据我了解,它在网络中不使用线程。但是,是否有一些内部线程并不重要,因为网络等子系统与主 nodejs JS 线程之间的所有通信都是通过事件队列进行的。

nodejs 的执行线程永远不会被中断来做其他事情。它完成并运行到完成,然后 JS 引擎检查事件队列以查看是否还有其他内容等待执行。

当套接字上有可用的传入数据时,将在事件队列中放置一个事件。当前正在执行的 nodejs Javascript 完成了它正在做的事情,然后 JS 引擎看到事件队列中有一个事件并触发该事件。如果有与该事件关联的函数回调或事件处理程序(通常有),则调用它来执行事件。

如果某些基础设施(例如网络)的内部发生故障,那么 nodejs 代码所发生的一切就是某些网络事件不会发生。nodejs 代码有它的事件处理程序,只是在基础设施取消楔入并创建事件之前不会接收他们正在等待的事件。这不会在 nodejs 代码中产生任何形式的挂起。

因此,在您的更新中:

来自评论和答案:我现在已经澄清了我的问题。“神秘场景”需要有一个隐藏线程(因此不能执行任何用户代码,包括回调)来构造请求,通过网络发送它并接收响应 - 没有任何回调触发,包括'data' 回调 - 并在'response' 回调准备好被调用时停止,等待(单个)可见 JS 线程唤醒。

nodejs 线程运行完成,然后 JS 引擎等待新的事件发生(例如放入事件队列中)。当该事件发生时,JS 引擎运行对应于该事件的代码(事件处理程序、回调等)。你让它听起来像单个可见的 JS 线程正在睡着等待唤醒,它可能会卡在那里,因为其他一些子系统被挂起。事实并非如此。唯一可能发生的事情是单个 JS 线程具有事件处理程序的某些事件永远不会发生。这与您向服务器发送消息并且您有一个事件处理程序来查看响应但服务器从不发送响应的情况没有什么不同。您的 nodejs 代码继续处理其他事件(计时器、其他网络、其他 I/O),但是这个特定事件永远不会发生,因为其他服务器从未发送过会触发该事件的数据。什么都挂着。

这是“事件 I/O”,这是 nodejs 描述自己的方式。

于 2015-06-18T04:42:23.360 回答
0

Node.js 中只涉及一个线程;事件循环用于处理异步运行的任务,排队的任何东西都不会中断已经运行的任何东西。所以不,那里没有竞争条件。

于 2015-06-18T04:05:21.057 回答