Skip to content

Error Handling

Vitaly Tomilov edited this page Nov 21, 2021 · 65 revisions

Due to the synchronous nature of iterables here, you can always resort to the regular try/catch at the point where iteration is triggered in your code. This however presents some problems:

  • Reusable iterables can trigger different errors, depending on usage, which means manual error handling will be required at every point of triggering an iteration.
  • Verbose error-handling coding around every use of iterables is a fairly poor and time-consuming practice.

That's why here we are covering error handling strictly during iteration, as values are retrieved one by one. This library supports explicit + implicit ways of handling iteration errors, as documented below.

Explicit error handling

You can handle all upstream iteration errors with a catchError operator at the end of the pipeline:

pipe(iterable, ...operators, catchError((error, ctx) => {
    // Handling the error (logging it, etc)

    // After that, you can any of the following:
    //  - re-throw the original error;
    //  - throw a new error;
    //  - do nothing (the value becomes simply skipped);
    //  - provide an alternative value, by calling ctx.emit(value)
}))

Parameters passed into the error handler:

  • error - the error that was thrown
  • index - index of the value that we failed to retrieve
  • lastValue - last value that we got successfully from the iterable, if there was any (undefined otherwise)

At the end, the callback is expected to do one of the following:

  • Re-throw the error that was passed in
  • Throw a new error
  • Return a new alternative value of the compatible type (as emitted by the preceding operator)

Implicit error handling

You can use catch on any piped iterable:

pipe(iterable, ...operators)
    .catch((error, index, lastValue) => {
    });

This will simply append catchError to the end of the pipeline, so the result is the same as with the explicit error handling. However, this adds the flexibility of handling errors separately from the pipeline implementation.

Chaining error handlers

You can chain as many error handlers as you want, each handling what's upstream, after the last error handler.

For explicit error handling, this means you can have any number of catchError operators inside the pipeline:

pipe(iterable, operator1, catchError, operator2, operator3, catchError);
//=> first catchError will handle errors thrown by operator1
//=> second catchError will handle errors thrown by operator2 and operator3
//=> second catchError will handle all errors, if the first catchError re-throws

For implicit error handling, this means you can chain any number of catch handlers at the end of the pipeline, each will be appending another catchError to the end of the pipeline:

pipe(iterable, ...operators)
    .catch() // first handler
    .catch() // second handler
    .catch() // third handler

// this will produce:
// pipe(iterable, ...operators, catchError, catchError, catchError);
Clone this wiki locally