“可读”事件发生两次

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()); }); 

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

据我所知,在调用read(10)后,stream必须停止并等待一个新的read()调用。 但是,调用的结果是:

 readable size = 10 0000000000 readable size = 10 0000000000 

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

stream动和非stream动模式

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

stream动模式

非stream动模式的示例,我们必须手动清空缓冲区(使用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 

非stream动模式

这是非stream动模式,因为我们不得不手动释放缓冲区。 现在我们将进入stream动模式。 在Readable Stream上设置data event listener器将stream从初始non-flowing mode切换到non-flowing mode 。 这意味着缓冲区将被自动清空。 Node.js会将数据作为parameter passing给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 

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

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

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

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

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

一个readable事件处理程序寄存器将触发read(0)函数来将数据从文件加载到内部缓冲区。 如果不覆盖highWaterMark的值,则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.ReadStream和stream.Readable 。