使用 node.js 流进行错误处理

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21771220/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 16:51:41  来源:igfitidea点击:

Error handling with node.js streams

node.jsstream

提问by B T

What's the correct way to handle errors with streams? I already know there's an 'error' event you can listen on, but I want to know some more details about arbitrarily complicated situations.

处理流错误的正确方法是什么?我已经知道有一个“错误”事件可以收听,但我想了解有关任意复杂情况的更多详细信息。

For starters, what do you do when you want to do a simple pipe chain:

首先,当你想做一个简单的管道链时,你会怎么做:

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

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

And how do you properly create one of those transforms so that errors are handled correctly?

您如何正确创建这些转换之一以便正确处理错误?

More related questions:

更多相关问题:

  • when an error happens, what happens to the 'end' event? Does it never get fired? Does it sometimes get fired? Does it depend on the transform/stream? What are the standards here?
  • are there any mechanisms for propogating errors through the pipes?
  • do domains solve this problem effectively? Examples would be nice.
  • do errors that come out of 'error' events have stack traces? Sometimes? Never? is there a way to get one from them?
  • 发生错误时,“结束”事件会发生什么?它永远不会被解雇吗?它有时会被解雇吗?它取决于转换/流吗?这里的标准是什么?
  • 是否有通过管道传播错误的机制?
  • 域能有效地解决这个问题吗?例子会很好。
  • 来自“错误”事件的错误是否有堆栈跟踪?有时?绝不?有没有办法从他们那里得到一个?

回答by mshell_lauren

transform

转变

Transform streams are both readable and writeable, and thus are really good 'middle' streams. For this reason, they are sometimes referred to as throughstreams. They are similar to a duplex stream in this way, except they provide a nice interface to manipulate the data rather than just sending it through. The purpose of a transform stream is to manipulate the data as it is piped through the stream. You may want to do some async calls, for example, or derive a couple of fields, remap some things, etc.

转换流既可读又可写,因此是非常好的“中间”流。出于这个原因,它们有时被称为through流。它们以这种方式类似于双工流,除了它们提供了一个很好的接口来操作数据而不是仅仅通过发送数据。转换流的目的是在数据通过流进行管道传输时对其进行操作。例如,您可能想要执行一些异步调用,或者派生几个字段、重新映射某些内容等。



Where you might put a transform stream

您可以将转换流放在哪里



For how to create a transform stream see hereand here. All you have to do is :

有关如何创建转换流的信息,请参见此处此处。您所要做的就是:

  1. include the stream module
  2. instantiate ( or inherit from) the Transform class
  3. implement a _transformmethod which takes a (chunk, encoding, callback).
  1. 包含流模块
  2. 实例化(或继承) Transform 类
  3. 实现一个_transform方法,它采用(chunk, encoding, callback).

The chunk is your data. Most of the time you won't need to worry about encoding if you are working in objectMode = true. The callback is called when you are done processing the chunk. This chunk is then pushed on to the next stream.

块是您的数据。大多数情况下,如果您使用objectMode = true. 处理完块后会调用回调。然后将该块推送到下一个流。

If you want a nice helper module that will enable you to do through stream really really easily, I suggest through2.

如果你想要一个很好的帮助模块,可以让你真的很容易地通过流来完成,我建议你through2

For error handling, keep reading.

对于错误处理,请继续阅读。

pipe

管道

In a pipe chain, handling errors is indeed non-trivial. According to this thread.pipe() is not built to forward errors. So something like ...

在管道链中,处理错误确实很重要。根据这个线程.pipe() 不是为了转发错误而构建的。所以像...

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

... would only listen for errors on the stream c. If an error event was emitted on a, that would not be passed down and, in fact, would throw. To do this correctly:

...只会监听流上的错误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)});

Now, though the second way is more verbose, you can at least keep the context of where your errors happen. This is usually a good thing.

现在,虽然第二种方式更冗长,但您至少可以保留错误发生的上下文。这通常是一件好事。

One library I find helpful though if you have a case where you only want to capture the errors at the destination and you don't care so much about where it happened is event-stream.

如果您只想捕获目标处的错误并且不太关心它发生的位置,我认为有一个库是event-stream

end

结尾

When an error event is fired, the end event will not be fired (explicitly). The emitting of an error event will end the stream.

当错误事件被触发时,结束事件将不会被触发(显式)。错误事件的发射将结束流。

domains

In my experience, domains work really well most of the time. If you have an unhandled error event (i.e. emitting an error on a stream without a listener), the server can crash. Now, as the above article points out, you can wrap the stream in a domain which should properly catch all errors.

根据我的经验,域在大多数情况下都运行良好。如果您有未处理的错误事件(即在没有侦听器的流上发出错误),服务器可能会崩溃。现在,正如上面的文章所指出的,您可以将流包装在一个域中,该域应该正确地捕获所有错误。

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

The beauty of domains is that they will preserve the stack traces. Though event-stream does a good job of this as well.

域的美妙之处在于它们将保留堆栈跟踪。尽管事件流在这方面也做得很好。

For further reading, check out the stream-handbook. Pretty in depth, but super useful and gives some great links to lots of helpful modules.

如需进一步阅读,请查看流手册。非常深入,但非常有用,并提供了许多有用模块的重要链接。

回答by shusson

If you are using node >= v10.0.0 you can use stream.pipelineand stream.finished.

如果您使用 node >= v10.0.0,则可以使用stream.pipelinestream.finished

For example:

例如:

const { pipeline, finished } = require('stream');

pipeline(
  input, 
  transformA, 
  transformB, 
  transformC, 
  (err) => {
    if (err) {
      console.error('Pipeline failed', err);
    } else {
      console.log('Pipeline succeeded');
    }
});


finished(input, (err) => {
  if (err) {
    console.error('Stream failed', err);
  } else {
    console.log('Stream is done reading');
  }
});

See this github PRfor more discussion.

有关更多讨论,请参阅此github PR

回答by Bent Cardan

domains are deprecated. you dont need them.

域已弃用。你不需要它们。

for this question, distinctions between transform or writable are not so important.

对于这个问题,transform 或 writable 之间的区别并不是那么重要。

mshell_lauren's answer is great, but as an alternative you can also explicitly listen for the error event on each stream you think might error. and reuse the handler function if you prefer.

mshell_lauren 的回答很好,但作为替代方案,您还可以显式侦听您认为可能出错的每个流上的错误事件。如果您愿意,可以重用处理程序函数。

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

doing so prevents the infamous uncaught exception should one of those stream fire its error event

如果这些流之一触发其错误事件,这样做可以防止臭名昭著的未捕获异常

回答by Gleba

Errors from the whole chain can be propagated to the rightmost stream using a simple function:

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

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

which can be used like:

可以像这样使用:

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

回答by Vikas Gautam

.on("error", handler)only takes care of Stream errors but if you are using custom Transform streams, .on("error", handler)don't catch the errors happening inside _transformfunction. So one can do something like this for controlling application flow :-

.on("error", handler)只处理 Stream 错误,但如果您使用自定义 Transform 流,.on("error", handler)请不要捕获_transform函数内部发生的错误。所以可以做这样的事情来控制应用程序流:-

thiskeyword in _transformfunction refers to Streamitself, which is an EventEmitter. So you can use try catchlike below to catch the errors and later on pass them to the custom event handlers.

this_transform函数中的关键字指的是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)

This way, you can keep your logic and error handlers separate. Also , you can opt to handle only some errors and ignore others.

这样,您可以将逻辑和错误处理程序分开。此外,您可以选择仅处理某些错误而忽略其他错误。

UPDATE
Alternative: RXJS Observable

更新
替代方案:RXJS Observable

回答by Sergey Savenko

Use multipipepackage to combinate several streams into one duplex stream. And handle errors in one place.

使用multipipe包将多个流合并为一个双工流。并在一处处理错误。

const pipe = require('multipipe')

// pipe streams
const stream = pipe(streamA, streamB, streamC) 


// centralized error handling
stream.on('error', fn)

回答by Derek

Use Node.js pattern by creating a Transform stream mechanics and calling its callback donewith an argument in order to propagate the error:

通过创建 Transform 流机制并done使用参数调用其回调来传播错误,从而使用 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);
   });

回答by Mehran

Try catch won't capture the errors that occurred in the stream because as they are thrown after the calling code has already exited. you can refer to the documentation:

Try catch 不会捕获流中发生的错误,因为它们是在调用代码已经退出后抛出的。你可以参考文档:

https://nodejs.org/dist/latest-v10.x/docs/api/errors.html

https://nodejs.org/dist/latest-v10.x/docs/api/errors.html