转换
转换流既可读又可写,因此它们是非常好的“中间”流。因此,它们有时被称为through
流。它们在这方面类似于双工流,但提供了一个良好的接口来操作数据,而不仅仅是通过发送数据。转换流的目的是在流传输数据时对其进行操作。例如,您可能想要进行一些异步调用,或者派生一些字段,重新映射一些内容等。
要创建一个转换流,请参见
这里和
这里。您需要做的只是:
- 包含stream模块
- 实例化(或继承)Transform类
- 实现一个
_transform
方法,该方法接受(chunk,encoding,callback)
。
chunk是您的数据。如果您在objectMode = true
中工作,则大多数时候无需担心编码问题。处理完成后将调用回调函数以将此块推到下一个流中。
如果您想要一个很好的助手模块来帮助您轻松地进行完全流操作,我建议使用through2。
有关错误处理,请继续阅读。
管道
在管道链中,处理错误确实是非常棘手的。根据
this thread所述,.pipe()不是为了转发错误而构建的。因此,类似于...
var a = createStream();
a.pipe(b).pipe(c).on('error', function(e){handleError(e)});
...只会在流c
上监听错误。如果a
上发出了错误事件,它不会被传递下去,实际上会抛出异常。要正确处理这个问题:
var a = createStream();
a.on('error', function(e){handleError(e)})
.pipe(b)
.on('error', function(e){handleError(e)})
.pipe(c)
.on('error', function(e){handleError(e)});
现在,虽然第二种方式更冗长,但至少你可以保留错误发生的上下文。这通常是一件好事。
有一个库我觉得很有用,如果你只想在目标位置捕获错误,而不太关心它发生在哪里,那就是event-stream。
结束
当触发错误事件时,将不会显式地触发结束事件。发生错误事件将终止流。
域
根据我的经验,域在大多数情况下都非常有效。如果出现未处理的错误事件(即在没有监听器的流上发出错误),服务器可能会崩溃。如上文所指出的,你可以将流包装在一个域中,以正确捕获所有错误。
var d = domain.create();
d.on('error', handleAllErrors);
d.run(function() {
fs.createReadStream(tarball)
.pipe(gzip.Gunzip())
.pipe(tar.Extract({ path: targetPath }))
.on('close', cb);
});
域名的美妙之处在于它们可以保留堆栈跟踪。虽然 event-stream 也做得很好。
如需进一步阅读,请查看stream-handbook1。非常深入,但非常有用,并提供了许多有用模块的链接。
1: 注意:由于原始 GitHub 存储库在2022年8月左右被删除,此链接指向archive.org。
Promise
框架使它变得简单许多。 - salezica