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
Subjectand 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 notificationvalueis greater than1the projection function returns the Observable created by thethrowError()operator. - We provide a the
errorOrErrorFactoryargument to thethrowError()operator. In this example we are providing anError. - Within the
mergeMap()operator, if thevalueis less-than1we return an Observable created by theof()operator that immediately emits a next notification of thevalue. - Next, we use the
tap()operator to log out thevalueusing theconsole.log()function. - Then, we subscribe to the Observable and provide the
Observerobject. - 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 theerrorvalue 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.