【问题标题】:"readable" event occurs twice“可读”事件发生两次
【发布时间】:2014-02-28 05:25:29
【问题描述】:
var fs = require('fs');

var file = fs.createReadStream('./zeros.txt');
var dataSize = 0;

file.on('readable', function () {
    var data = file.read(10);
    console.log('readable size = ', data.length);
    console.log(data.toString());
});

文件“zeros.txt”包含 700 个字符“0”

据我了解,在调用 read(10) 之后,流必须停止并等待新的 read() 调用。但是调用的结果:

readable size =  10
0000000000
readable size =  10
0000000000

【问题讨论】:

    标签: node.js stream node.js-stream


    【解决方案1】:

    在 Node.js 将文件(整个或只是一部分,取决于文件本身的大小)加载到缓冲区后(使用 push() 方法),它将发出 readable event 以指示某些数据已已读入缓冲区并可以使用。然后在您调用read(10) 之后,您将释放缓冲区,然后Node.js 将再次自动填充缓冲区并再次发出readable event 以指示仍有一些数据要从缓冲区读取。如果您调用read(700),则不会再次发出下一个readable event

    流动和非流动模式

    与收听data event 时不同,流将保持所谓的非流动模式。这意味着开发人员将负责释放流(从流中读取)。另一方面,当收听data event 时,流将自动进入所谓的流动模式,这意味着流本身将负责释放自己,即流将填充和清空自己,直到底层系统(在这种情况下zero.txt 将被完全阅读)。请注意,无论哪种模式,缓冲区都会自动填充数据。

    流动模式

    一个非流动模式的例子,我们必须手动清空缓冲区(使用read()方法):

    var fs = require('fs'),
    util = require('util');
    
    // I have increased the file size to 19 MB (about 19 mln characters);
    // Cause of the buffer capicity.
    var file = fs.createReadStream('./zeros.txt'); 
    var dataSize = 0;
    
    // Readable will be called when the buffer has been filled with data.
    // Initially Node.js will fill the buffer with data automatically,
    // so this event will be called automatically aswell of course.
    // Once the buffer will be free again after the first fill, Node.js
    // will fill the buffer automatically again. Node.js just watches this stream
    // and makes sure to fill it, when there is still some unread data in the zero.txt file.
    file.on('readable', function() {
    var i = 0; // we will count how many times did while loop, for fun
    
    // If the buffer will be empty Node will write data to the buffer
    // automatically, we don't have to care about that. However
    // you can specify the buffer capicty manually if you want.
    console.log('loading more data from the underlying system');
    
    // This will make the stream read 1000 bytes
    // it will also return a value NULL if there is not enough 
    // data to read from the buffer (meaning buffer has been fully read 
    // or there is still some data but you are trying to read 1000 bytes 
    // and there is less than 1000 bytes left)
    while(file.read(1000) !== null) {
        i++;
    }
    // At this moment while loop has read everything from the buffer.
    // The buffer is now empty. After this comment console.log will execute
    // Node.js will fill the buffer again with new data automatically.
    // And then the 'readable' event will fire again.
    console.log("had to loop: " + i + " times before the buffer was empty");
    })
    

    控制台的最后几个结果:

    loading more data from the underlying system
    had to loop: 66 times before the buffer was empty
    loading more data from the underlying system
    had to loop: 65 times before the buffer was empty
    loading more data from the underlying system
    had to loop: 66 times before the buffer was empty
    loading more data from the underlying system
    had to loop: 46 times before the buffer was empty
    loading more data from the underlying system
    had to loop: 1 times before the buffer was empty
    

    非流动模式

    那是非流动模式,因为我们必须手动释放缓冲区。现在我们将进入流动模式。在Readable Stream 上设置data event listener 会将流从初始non-flowing mode 切换到flowing mode。这意味着缓冲区将被自动清空。 Node.js 会将数据作为data event listener 中的参数传递给您,一旦该函数执行,缓冲区将再次清空,如果底层源缓冲区中仍有一些数据,则会自动填充新数据,然后数据事件将再次发出。注意:如果您正在监听data 事件并且readable event 都将触发,但data event listener 将首先清空缓冲区,然后readable event 将触发,因此您的read() 将始终返回NULL

    var fs = require('fs'),
    util = require('util');
    
    var file = fs.createReadStream('./zeros.txt');
    var dataSize = 0;
    
    file.on('data', function() {
        // Once this listener will stop executing new data will be read
        // into the buffer and then the 'data' event will be emitted
        // again.
        console.log('data has been loaded and emptied!')
    })
    
    file.on('readable', function () {
        // Notice we want to try to read 1 byte from the buffer
        // but in the console we see that the read() method
        // resulted in NULL, which means that the buffer is empty.
        // That's of course because we enterd the flowing mode
        // by setting up the 'data' event. (In flowing mode)
        // after the execution of the 'data' event all data
        // from the buffer will be read, but the execution
        // of listeners will continue. After all the event listeners
        // attached to this stream will execute, Node.js will fill
        // the buffer automatically again.
        console.log('readable ' + file.read(1))
    });
    

    控制台的最后几个结果:

    data has been loaded and emptied!
    readable null
    data has been loaded and emptied!
    readable null
    data has been loaded and emptied!
    readable null
    data has been loaded and emptied!
    readable null
    data has been loaded and emptied!
    readable null
    

    【讨论】:

      【解决方案2】:

      我的回答是基于0.12.4的版本。

      1:当当前内部缓冲区长度为0或小于highWaterMark属性的值时,从Stream.Readable扩展的每个read(n)函数都会触发内部_read(n)函数.

      2:readable事件只会在当前内部缓冲区长度为0或从内部缓冲区读取的数据为空或出现空指示符时触发。

      我们以你的代码为例,看看发生了什么。

      file.on('readable', function () {
      

      readable 事件处理程序寄存器将触发 read(0) 函数将数据从文件加载到内部缓冲区。如果你不覆盖 highWaterMark 的值,它最多会加载 64 * 1024 = 65535 个块。在您的代码中,它加载了文件“zeros.txt”中的所有数据。之后会触发readable事件,因为在调用read(0)函数之前内部缓冲区长度为0。

      var data = file.read(10);
      

      在处理程序中,您再次调用了read(10) 函数。这也会触发从文件到缓冲区的加载过程。但是,此时不会加载任何数据。因此,null会被推送,表示读取过程已经完成。第二个readable 事件已被触发。这就是您应该看到并且只看到两个readable 事件的原因。

      如果您读取的文件大小大于 65535 字节(几乎 66KB),您应该只会看到一个 readable 事件被触发。

      你不应该这样写readable事件处理程序,你应该参考以下内容:

      var chunk;
      while( null !== ( chunk = fs.read() ) ) {
          //handle the chunk
      }
      

      如果你想以自己的方式处理块做一些特殊的事情,请注意规则;否则,程序将停留在“暂停”状态,不再读取数据,也不再检索数据。

      请参阅fs.ReadStreamstream.Readable

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-08-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-11-29
        • 2014-04-07
        相关资源
        最近更新 更多