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