用node.jsstream处理错误

什么是正确的方式来处理stream错误? 我已经知道有一个你可以听的“错误”事件,但是我想知道更多关于任意复杂情况的细节。

对于初学者,当你想做一个简单的pipe道链时,你会做什么:

input.pipe(transformA).pipe(transformB).pipe(transformC)...

那么如何正确地创build这些转换之一,以便正确处理错误?

更多相关问题:

  • 当发生错误时,“结束”事件会发生什么? 它不会被解雇吗? 有时会被解雇吗? 它依赖于变换/stream? 这里的标准是什么?
  • 是否有任何机制通过pipe道传播错误?
  • 域有效地解决这个问题吗? 例子会很好。
  • “错误”事件产生的错误是否有堆栈跟踪? 有时? 决不? 有没有办法从他们那里得到一个?

转变

转换stream是可读和可写的,因此是非常好的“中间”stream。 出于这个原因,他们有时被称为throughstream。 它们与这种方式类似于双工蒸汽,只不过它们提供了一个很好的界面来操纵数据,而不仅仅是发送数据。变换stream的目的是在数据通过stream时进行操作。 例如,您可能想要进行一些asynchronous调用,或者派生一些字段,重新映射某些内容等。


你可能会放置一个转换流


有关如何创build变换stream,请参阅此处和此处 。 你所要做的就是:

  1. 包括stream模块
  2. 实例化(或inheritance)Transform类
  3. 实现一个采用(chunk, encoding, callback)_transform方法。

块是你的数据。 大多数情况下,如果您在objectMode = true中工作,则无需担心编码。 当您完成处理块时调用callback。 这个块然后被推送到下一个stream。

如果你想要一个很好的帮手模块,可以让你真的很容易地通过stream,我build议通过2 。

为了处理错误,请继续阅读。

pipe

在pipe道链中,处理错误确实是不重要的。 根据这个线程 .pipe()不是用来转发错误的。 所以像这样:

 var a = createStream(); a.pipe(b).pipe(c).on('error', function(e){handleError(e)}); 

只会听streamc上的错误。 如果一个错误事件发生在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)}); 

现在,尽pipe第二种方式更加冗长,但至less可以保持发生错误的上下文。 这通常是一件好事。

一个我觉得有用的图书馆,如果你有一个情况,你只想捕捉在目的地的错误,你不太在乎它发生的地方是事件stream 。

结束

当发生错误事件时,结束事件不会被触发(明确)。 发生错误事件将结束stream。

根据我的经验,域名在大多数情况下工作得很好。 如果你有一个未处理的错误事件(即在没有监听器的情况下在一个stream上发送错误),服务器可能会崩溃。 现在,正如上面的文章指出的那样,你可以将这个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); }); 
  • 上面的代码示例来自这个post

领域之美是他们将保留堆栈痕迹。 尽pipe事件stream也做得很好。

更多的阅读结算stream手册 。 相当深入,但超级有用,并提供了很多有用的模块的一些很好的链接。

域被弃用。 你不需要他们。

对于这个问题,转换或可写的区别并不那么重要。

mshell_lauren的答案很好,但是作为一种select,你也可以明确地监听你认为可能出错的每个stream上的错误事件。 并重用处理函数,如果你喜欢。

 var a = createReadableStream() var b = anotherTypeOfStream() var c = createWriteStream() a.on('error', handler) b.on('error', handler) c.on('error', handler) a.pipe(b).pipe(c) function handler (err) { console.log(err) } 

这样做可以防止臭名昭着的未捕获的exception,如果其中一个stream引发错误事件

整个链中的错误可以使用一个简单的函数传播到最右边的stream:

 function safePipe (readable, transforms) { while (transforms.length > 0) { var new_readable = transforms.shift(); readable.on("error", function(e) { new_readable.emit("error", e); }); readable.pipe(new_readable); readable = new_readable; } return readable; } 

可以像这样使用:

 safePipe(readable, [ transform1, transform2, ... ]); 

.on("error", handler).on("error", handler) Stream错误,但如果使用自定义的Transformstream, .on("error", handler)不会捕获_transform函数内部发生的错误。 所以可以这样做来控制应用程序stream:

_transform函数中的this关键字指的是Stream本身,它是一个EventEmitter 。 所以你可以使用下面的try catch来捕获错误,稍后将它们传递给自定义事件处理程序。

 // CustomTransform.js CustomTransformStream.prototype._transform = function (data, enc, done) { var stream = this try { // Do your transform code } catch (e) { // Now based on the error type, with an if or switch statement stream.emit("CTError1", e) stream.emit("CTError2", e) } done() } // StreamImplementation.js someReadStream .pipe(CustomTransformStream) .on("CTError1", function (e) { console.log(e) }) .on("CTError2", function (e) { /*Lets do something else*/ }) .pipe(someWriteStream) 

这样,你可以保持你的逻辑和error handling程序分开。 此外,您可以select只处理一些错误,并忽略其他人。

通过创build一个Transformstream机制并使用参数调用其callback函数来传播错误,从而使用Node.js模式:

 var transformStream1 = new stream.Transform(/*{objectMode: true}*/); transformStream1.prototype._transform = function (chunk, encoding, done) { //var stream = this; try { // Do your transform code /* ... */ } catch (error) { // nodejs style for propagating an error return done(error); } // Here, everything went well done(); } // Let's use the transform stream, assuming `someReadStream` // and `someWriteStream` have been defined before someReadStream .pipe(transformStream1) .on('error', function (error) { console.error('Error in transformStream1:'); console.error(error); process.exit(-1); }) .pipe(someWriteStream) .on('close', function () { console.log('OK.'); process.exit(); }) .on('error', function (error) { console.error(error); process.exit(-1); });