lookout.devlookout.dev
search
Share Knowledge
00

How do I throw an exception in RxJS?

Friday, June 4, 2021

How do I throw an exception in RxJS?

This is a common question I've heard when teaching others about RxJS. Perhaps the more pointed question is: what is the best approach to throwing an exception?

throw Statement

Every operator that accepts a callback function wraps the execution of the function in a try...catch block. Knowing this, the easiest approach to throwing an exception is to use JavaScript's native throw statement.

Let's look at an example.

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(e),
    next: console.log,
    complete: () => console.log('complete')
  });

subject.emit(2);
subject.emit(2);

In this example, we first create a new instance of a Subject whose next notification value type is a number. Note that within the tap() operator's project function we have some logical check, while trivial in this example, we use the throw statement to trigger an error notification and the termination of the Observable.

In my opinion, this is often the best approach to throwing an exception with RxJS.

throwError() Operator

While my first choice is to use JavaScript's native throw statement, we can also use RxJS's provided throwError() operator. The benefit of using the throwError() operator is the Observable that is created by the operator. The operator returns a new Observable that immediately emits an error notification, and thus, terminates the Observable.

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

const subject = new Subject<number>();

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

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

In this example, we are using the throwError() operator to throw an exception. Because the operator returns an Observable we can make use of the pipe() method to perform additional side effects that might be necessary, perhaps notifying a backend service of the exception.

In most instances, just use the throw statement unless you need the Observable that is created by the throwError() operator.

Instructions

question-circle
Consider

Using the throw statement over the throwError() operator.

Brian Love

I am a software engineer and Google Developer Expert in Web Technologies and Angular with a passion for learning, writing, speaking, teaching and mentoring. I regularly speaks at conferences and meetups around the country, and co-authored "Why Angular for the Enterprise" for O'Reilly. When not coding, I enjoy skiing, hiking, and being in the outdoors. I started lookout.dev to break down the barriers of learning in public. Learning in public fosters growth - for ourselves and others.

Google Developers Expert

Have a question or comment?

You

I love to read every small approach rather big one.

Brian Love
Brian Love

Thanks, Milad!

You

Hi Brian, in the first example your description text mentions "map" but there is no "map" in the code example.

Brian Love
Brian Love

Gil, good catch. I've updated this to reflect the use of the tap() operator.

You

Brian, I was playing around with the simple example in Stackblitz and I had to use "next" instead of "emit".

https://stackblitz.com/edit/rxjs-exceptions