如何在Node.js中使用Stream.Writable的drain事件

在Node.js中,我使用fs.createWriteStream方法将数据附加到本地文件。 在Node文档中,他们提到使用fs.createWriteStream时的drain事件,但我不明白。

 var stream = fs.createWriteStream('fileName.txt'); var result = stream.write(data); 

在上面的代码中,如何使用漏极事件? 下面的事件是否正确使用?

 var data = 'this is my data'; if (!streamExists) { var stream = fs.createWriteStream('fileName.txt'); } var result = stream.write(data); if (!result) { stream.once('drain', function() { stream.write(data); }); } 

当可写stream的内部缓冲区被清空时, drain事件是用于。

只有当内部缓冲区的大小超过其highWaterMark属性时,才会发生这种情况, highWaterMark属性是可写入stream的内部缓冲区中可以存储的最大字节数,直到停止从数据源读取。

造成这种情况的原因可能是由于涉及从一个数据stream读取数据源的设置速度快于可以写入另一个数据源的设置。 例如,采取两个stream:

 var fs = require('fs'); var read = fs.createReadStream('./read'); var write = fs.createWriteStream('./write'); 

现在想象一下, read的文件在SSD上,可以以500MB / s的速度读取, write的硬盘只能以150MB/s写入。 写入stream将无法跟上,并开始将数据存储在内部缓冲区中。 一旦缓冲区达到highWaterMark (默认为16KB),写入将开始返回false ,并且该stream将在内部排队排水。 一旦内部缓冲区的长度为0,那么drain事件就会被触发。

这是一个排水pipe的工作原理:

 if (state.length === 0 && state.needDrain) { state.needDrain = false; stream.emit('drain'); } 

这些是writeOrBuffer函数的一部分漏极的先决条件:

 var ret = state.length < state.highWaterMark; state.needDrain = !ret; 

要查看如何使用drain事件,请从Node.js文档中获取示例。

 function writeOneMillionTimes(writer, data, encoding, callback) { var i = 1000000; write(); function write() { var ok = true; do { i -= 1; if (i === 0) { // last time! writer.write(data, encoding, callback); } else { // see if we should continue, or wait // don't pass the callback, because we're not done yet. ok = writer.write(data, encoding); } } while (i > 0 && ok); if (i > 0) { // had to stop early! // write some more once it drains writer.once('drain', write); } } } 

函数的目标是写一百万次到一个可写的stream。 会发生什么变化ok被设置为true,并且一个循环只有在ok为真时才会被执行。 对于每个循环迭代, ok的值被设置为stream.write()的值,如果需要drain ,将返回false。 如果ok成为错误,则drain事件处理程序等待,并着火,恢复写作。


特别是关于你的代码,你不需要使用drain事件,因为你打开你的stream后只写一次。 由于你还没有写入任何东西到stream中,内部缓冲区是空的,你将不得不写入至less16KB的大块,以便drain事件触发。 drain事件用于写入多次数据,而不是写入数据stream的highWaterMark设置。

想象一下,您将连接2个带宽非常不同的stream,例如将本地file upload到慢速服务器。 (快速)文件stream比(慢)套接字stream可以消耗更快的数据。

在这种情况下,node.js会将数据保存在内存中,直到缓慢的stream得到处理它的机会。 如果文件非常大,这可能会有问题。

为避免这种情况,当底层系统缓冲区已满时, Stream.write将返回false 。 如果停止写入,则stream将稍后发出drain事件,以指示系统缓冲区已清空,并且适合再次写入。

您可以使用pause/resume可读stream,并控制可读stream的带宽。

更好:你可以使用readable.pipe(writable) ,这将为你做这个。

编辑 :你的代码中有一个错误:无论write什么回报,你的数据已被写入。 你不需要重试它。 在你的情况下,你正在写data两次。

像这样的东西可以工作:

 var packets = […], current = -1; function niceWrite() { current += 1; if (current === packets.length) return stream.end(); var nextPacket = packets[current], canContinue = stream.write(nextPacket); // wait until stream drains to continue if (!canContinue) stream.once('drain', niceWrite); else niceWrite(); }