1 import { Operator } from '../Operator';
2 import { Subscriber } from '../Subscriber';
3 import { Observable } from '../Observable';
5 import { OuterSubscriber } from '../OuterSubscriber';
6 import { InnerSubscriber } from '../InnerSubscriber';
7 import { subscribeToResult } from '../util/subscribeToResult';
8 import { ObservableInput, OperatorFunction, ObservedValueOf } from '../types';
10 /* tslint:disable:max-line-length */
11 export function catchError<T, O extends ObservableInput<any>>(selector: (err: any, caught: Observable<T>) => O): OperatorFunction<T, T | ObservedValueOf<O>>;
12 /* tslint:enable:max-line-length */
15 * Catches errors on the observable to be handled by returning a new observable or throwing an error.
20 * Continues with a different Observable when there's an error
23 * import { of } from 'rxjs';
24 * import { map, catchError } from 'rxjs/operators';
26 * of(1, 2, 3, 4, 5).pipe(
33 * catchError(err => of('I', 'II', 'III', 'IV', 'V')),
35 * .subscribe(x => console.log(x));
36 * // 1, 2, 3, I, II, III, IV, V
39 * Retries the caught source Observable again in case of error, similar to retry() operator
42 * import { of } from 'rxjs';
43 * import { map, catchError, take } from 'rxjs/operators';
45 * of(1, 2, 3, 4, 5).pipe(
52 * catchError((err, caught) => caught),
55 * .subscribe(x => console.log(x));
56 * // 1, 2, 3, 1, 2, 3, ...
59 * Throws a new error when the source Observable throws an error
62 * import { of } from 'rxjs';
63 * import { map, catchError } from 'rxjs/operators';
65 * of(1, 2, 3, 4, 5).pipe(
73 * throw 'error in source. Details: ' + err;
77 * x => console.log(x),
78 * err => console.log(err)
80 * // 1, 2, 3, error in source. Details: four!
83 * @param {function} selector a function that takes as arguments `err`, which is the error, and `caught`, which
84 * is the source observable, in case you'd like to "retry" that observable by returning it again. Whatever observable
85 * is returned by the `selector` will be used to continue the observable chain.
86 * @return {Observable} An observable that originates from either the source or the observable returned by the
87 * catch `selector` function.
90 export function catchError<T, O extends ObservableInput<any>>(
91 selector: (err: any, caught: Observable<T>) => O
92 ): OperatorFunction<T, T | ObservedValueOf<O>> {
93 return function catchErrorOperatorFunction(source: Observable<T>): Observable<T | ObservedValueOf<O>> {
94 const operator = new CatchOperator(selector);
95 const caught = source.lift(operator);
96 return (operator.caught = caught as Observable<T>);
100 class CatchOperator<T, R> implements Operator<T, T | R> {
101 caught: Observable<T>;
103 constructor(private selector: (err: any, caught: Observable<T>) => ObservableInput<T | R>) {
106 call(subscriber: Subscriber<R>, source: any): any {
107 return source.subscribe(new CatchSubscriber(subscriber, this.selector, this.caught));
112 * We need this JSDoc comment for affecting ESDoc.
116 class CatchSubscriber<T, R> extends OuterSubscriber<T, T | R> {
117 constructor(destination: Subscriber<any>,
118 private selector: (err: any, caught: Observable<T>) => ObservableInput<T | R>,
119 private caught: Observable<T>) {
123 // NOTE: overriding `error` instead of `_error` because we don't want
124 // to have this flag this subscriber as `isStopped`. We can mimic the
125 // behavior of the RetrySubscriber (from the `retry` operator), where
126 // we unsubscribe from our source chain, reset our Subscriber flags,
127 // then subscribe to the selector result.
129 if (!this.isStopped) {
132 result = this.selector(err, this.caught);
137 this._unsubscribeAndRecycle();
138 const innerSubscriber = new InnerSubscriber(this, undefined, undefined);
139 this.add(innerSubscriber);
140 const innerSubscription = subscribeToResult(this, result, undefined, undefined, innerSubscriber);
141 // The returned subscription will usually be the subscriber that was
142 // passed. However, interop subscribers will be wrapped and for
143 // unsubscriptions to chain correctly, the wrapper needs to be added, too.
144 if (innerSubscription !== innerSubscriber) {
145 this.add(innerSubscription);