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);
Now, let's break this down:
- First, we create a new
Subject
and specify the generic type for next notifications tonumber
. - Within the
pipe()
operator we first use themergeMap()
operator. ThemergeMap()
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 notificationvalue
is greater than1
the projection function returns the Observable created by thethrowError()
operator. - We provide a the
errorOrErrorFactory
argument to thethrowError()
operator. In this example we are providing anError
. - Within the
mergeMap()
operator, if thevalue
is less-than1
we return an Observable created by theof()
operator that immediately emits a next notification of thevalue
. - Next, we use the
tap()
operator to log out thevalue
using theconsole.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 value1
. Secondly, we emit the value2
We should expect the following to occur in the console:
- The first next notification value is
1
, therefore, themergeMap()
operator returns an Observable that emits thevalue
. As such, we then expect that thetap()
operator will log the value to the console. - The Observer's
next()
function is then invoked with the value of1
. - The second next notification value is
2
, therefore themergeMap()
operator returns an Observable that immediately emits an error notification. As such, we then expect that theerror()
function of the Observer is invoked with theerror
value that is produced bythrowError()
.
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);
The code above results in the same behavior as before where we used the throwError()
operator, and is potentially easier to read.