【问题标题】:Node read stream: when does the streaming happen?节点读取流:流何时发生?
【发布时间】:2014-12-19 00:40:34
【问题描述】:

这是一个代码示例,与您从网上或文档中获得的内容没有太大区别:

var fs = require('fs');
var r = fs.createReadStream(process.argv[2], { encoding: 'utf8' });
r.on('data', function (chunk) {
    console.log("chunk: >>>" + chunk + "<<<");
});
r.on('end', function () {
    console.log("This is the end");
});

让我感到困惑的是:触发事件的流式传输何时发生?显然不是直接在读取流的构造上,因为它会在我们到达ons 之前完成,并且永远不会执行事件监听代码(确实如此,这完美地工作)。

让我担心的是:如果on 呼叫来得太晚,理论上是否有可能错过某个事件?

【问题讨论】:

    标签: javascript node.js events stream fs


    【解决方案1】:

    想到这一点的最简单方法是,您提供的所有代码都是阻塞的,因此在当前滴答结束之前,流上不会发生任何活动,正如 vkurchatkin 所解释的那样。只有当当前刻度的 JavaScript 执行完成时,才能开始流 IO。

    var fs = require('fs');
    var r = fs.createReadStream(process.argv[2], { encoding: 'utf8' });
    // Stream created in paused state, JS code is blocking IO
    
    // We're still in the current tick so no IO could have occured since the above lines
    r.on('data', function (chunk) {
      console.log("chunk: >>>" + chunk + "<<<");
    });
    
    // We're still in the current tick so no IO could have occured since the above lines
    r.on('end', function () {
      console.log("This is the end");
    });
    
    // We've left the current tick so the internal IO code will now execute and call the bound events if necessary
    

    【讨论】:

    • 但是为什么我们把当前刻度留在最后一行?关键点是触发滴答结束的原因,如果这是流式传输开始的地方。
    • 当没有更多你绑定的事件被执行时。换句话说,一旦所有的 JS 代码都执行完毕,下一个滴答声就会发生。这可能不是完美的技术解释,但足以理解这个概念。
    • “你已经绑定的事件”,这是一些我不熟悉的术语。好吧,我理解这个概念,我会在其他时间弄清楚到底发生了什么(包括 I/O 等待和计时器)。
    【解决方案2】:

    答案是否定的,在节点 0.10.x 及更高版本中是不可能的。创建流时,它会暂停,因此dataend 事件都不能发出。当您添加data 侦听器(但不是end 侦听器)时,流会自动恢复。

    还值得一提的是,在当前“tick”结束之前不会发生 IO,因此如果您在同一个 tick 中附加 data 侦听器始终是安全的,即使对于早期的节点版本也是如此。例如:

    stream.resume();
    stream.on('data', ...); // <- same tick, same javascript invocation = safe
    
    stream.resume();
    setImmediate(function () {
      stream.on('data', ...); // <- different tick, different javascript invocation = unsafe
    });
    

    这听起来可能令人困惑,但在 process.nextTick 回调中添加监听器也是安全的,因为它实际上是在任何 IO 回调之前的 CURRENT 滴答之后立即调用的(命名非常糟糕的情况)。

    【讨论】:

    • 从您的第一段开始,听起来'end' 事件仍然可能丢失,因为当我添加'data' 侦听器时流会恢复,并且可能在我添加@987654330 之前完成@听众。我想你在第二段中所说的话可以防止这种情况发生,如果两个ons 都保证在同一个刻度中。但是在同一个tick中执行两条语句的标准是什么?
    • 没错,如果你添加data监听器,而不是end监听器,你可能会错过它。更新了答案
    • 我仍然感到困惑的是,在同一滴答中执行两条语句的标准是什么。从您的示例中,我认为on 不会使滴答声结束,但setImmediate 会。那么到底是什么使滴答声结束呢?我也无法通过谷歌搜索找到答案,尽管我认为它存在于一些很长很深的文本中。
    • 试试这个:console.log(new Error().stack)。您将看到当前堆栈跟踪。当函数返回时,堆栈跟踪减少了一行(帧)。当堆栈为空时,当前滴答结束。
    • 所以你是说在当前的执行链完全耗尽之前不会发生上下文切换? “滴答”就像完整的线程,按顺序执行?
    猜你喜欢
    • 2016-11-07
    • 1970-01-01
    • 1970-01-01
    • 2017-08-26
    • 2015-11-14
    • 1970-01-01
    • 1970-01-01
    • 2019-08-18
    • 2011-01-18
    相关资源
    最近更新 更多