LiveLoveApp logo

ThrowError Operator

throwError() Operator

To start with, you can almost think of the throwError() operator as similar to the built-in throw operator in JavaScript.

The throwError() operator creates an Observable that emits an error notification immediately upon subscribing. This is particularly useful when we want to throw an error within a sequence of operators under a particular condition. We can also use the throwError() operator to short-circuit a function that is expected to return an Observable by returning the Observable created by throwError() before executing subsequent statements in the function.

Example

Let's look an example of using the throwError() operator:

import { of, Subject, throwError } from 'rxjs';
import { mergeMap, tap } from 'rxjs/operators';

const subject = new Subject<number>();

subject
  .pipe(
    mergeMap((value) =>
      value > 1
        ? throwError(new Error('Error emitted by throwError'))
        : of(value)
    ),
    tap((value) => console.log('tap', value))
  )
  .subscribe({
    error: (e) => console.error('observer', e),
    next: (value) => console.log('next', value),
    complete: () => console.log('complete')
  });

subject.next(1);
subject.next(2);

See example on codesandbox

Now, let's break this down:

  • First, we create a new Subject and specify the generic type for next notifications to number.
  • Within the pipe() operator we first use the mergeMap() operator. The mergeMap() operator invokes a projection function that returns an Observable for each next notification, and returns a new Observable that emits next notifications merged from each Observable returned from the projection function. In this example, if the next notification value is greater than 1 the projection function returns the Observable created by the throwError() operator.
  • We provide a the errorOrErrorFactory argument to the throwError() operator. In this example we are providing an Error.
  • Within the mergeMap() operator, if the value is less-than 1 we return an Observable created by the of() operator that immediately emits a next notification of the value.
  • Next, we use the tap() operator to log out the value using the console.log() function.
  • Then, we subscribe to the Observable and provide the Observer object.
  • Finally, we use the next() method to emit two next notification. First, we emit the value 1. Secondly, we emit the value 2

We should expect the following to occur in the console:

  1. The first next notification value is 1, therefore, the mergeMap() operator returns an Observable that emits the value. As such, we then expect that the tap() operator will log the value to the console.
  2. The Observer's next() function is then invoked with the value of 1.
  3. The second next notification value is 2, therefore the mergeMap() operator returns an Observable that immediately emits an error notification. As such, we then expect that the error() function of the Observer is invoked with the error value that is produced by throwError().

Error Factory

The throwError() operator accepts either an error value, or a factory function that produces an error value.

For example, we can perform any statements as necessary within the factory function before returning the error value that is emitted as an error notification upon subscribing:

observable.pipe(
  mergeMap(id =>
    id === null
      ? throwError(() => {
          const error = new Error('id is null');
          errorService.notify(error)
          return error
      })
     : fetchUser(id)
  )
).subscribe();

In the example above we are providing a factory function to the throwError() operator in order to perform a side effect, namely notifying our errorService of the error.

JavaScript's throw Operator

The throwError operator is very useful because it returns a new Observable that immediately emits an error notification. This enables us to leverage the Observable that is returned from the throwError operator.

If we do not need this functionality of the throwError() operator, it is likely easier, and perhaps an improvement in code readability, to use JavaScript's throw operator.

import { Subject } from 'rxjs';
import { tap } from 'rxjs/operators';

const subject = new Subject<number>();

subject
  .pipe(
    tap((value) => {
      if (value > 1) {
        throw new Error('Error emitted by throwError');
      }
    }),
    tap((value) => console.log('tap', value))
  )
  .subscribe({
    error: (e) => console.error('observer', e),
    next: (value) => console.log('next', value),
    complete: () => console.log('complete')
  });

subject.next(1);
subject.next(2);

See example on codesandbox

The code above results in the same behavior as before where we used the throwError() operator, and is potentially easier to read.