caolan / highland

High-level streams library for Node.js and the browser

Home Page:https://caolan.github.io/highland

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Error propagation and Node streams interop

fbiville opened this issue Β· comments

Hi,
First, thanks for this great library. It's great to be able to work with built-in Node streams while enjoying higher-level functional operators πŸ‘πŸ»

I am using Highland.js 2.13.5.

I'm currently having issues with the following function:

(inputStreams, outputStreams) => {
                const numberStream = _(inputStreams["0"]);
                const wordStream = _(inputStreams["1"]);
                numberStream
                    .zip(wordStream)
                    .flatMap((numberWordPair) => {
                        const result = [];
                        for (let i = 0; i < numberWordPair[0]; i++) {
                            result.push(numberWordPair[1]);
                        }
                        return _(result);
                    })
                    .pipe(outputStreams["0"]);
            }

inputStreams["0"], inputStreams["1"] and outputStreams["0"] all have an error handler installed with .on('error', (err) => {...}).

If I send a bogus input, though, the whole process crashes.

As the pipe doc says:

Like Readable#pipe, this function will throw errors if there is no error handler installed on the stream.

A quick fix is to call .errors in the pipeline and e.g. discard the error.

The question is: why do I need .errors if all the streams already have an error handler?

The source streams have error handlers, but not the consuming Highland stream.

When you wrap a Readable, Highland will bind an error handler and propagate the result to downstream consumers. So when you pipe to outputStreams, the same errors are thrown.

Node Readables allow you to bind multiple error handlers independently, so Highland has no way of knowing that you've already bound the error handlers.

If your intent it to discard the errors from the inputStreams, call .errors on the resulting highland streams instead.

(inputStreams, outputStreams) => {
                const numberStream = _(inputStreams["0"]).errors(ignore);
                const wordStream = _(inputStreams["1"]).errors(ignore);
                numberStream
                    .zip(wordStream)
                    .flatMap((numberWordPair) => {
                        const result = [];
                        for (let i = 0; i < numberWordPair[0]; i++) {
                            result.push(numberWordPair[1]);
                        }
                        return _(result);
                    })
                    // You won't see errors from the inputStreams here.
                    .pipe(outputStreams["0"]);
            }

Thanks for the detailed explanation!