import { Observable } from '../Observable'; | |
import { ObservableInput, OperatorFunction, ObservedValueOf } from '../types'; | |
import { Subscription } from '../Subscription'; | |
import { innerFrom } from '../observable/innerFrom'; | |
import { createOperatorSubscriber } from './OperatorSubscriber'; | |
import { operate } from '../util/lift'; | |
/* tslint:disable:max-line-length */ | |
export function catchError<T, O extends ObservableInput<any>>( | |
selector: (err: any, caught: Observable<T>) => O | |
): OperatorFunction<T, T | ObservedValueOf<O>>; | |
/* tslint:enable:max-line-length */ | |
/** | |
* Catches errors on the observable to be handled by returning a new observable or throwing an error. | |
* | |
* <span class="informal"> | |
* It only listens to the error channel and ignores notifications. | |
* Handles errors from the source observable, and maps them to a new observable. | |
* The error may also be rethrown, or a new error can be thrown to emit an error from the result. | |
* </span> | |
* | |
*  | |
* | |
* This operator handles errors, but forwards along all other events to the resulting observable. | |
* If the source observable terminates with an error, it will map that error to a new observable, | |
* subscribe to it, and forward all of its events to the resulting observable. | |
* | |
* ## Examples | |
* | |
* Continue with a different Observable when there's an error | |
* | |
* ```ts | |
* import { of, map, catchError } from 'rxjs'; | |
* | |
* of(1, 2, 3, 4, 5) | |
* .pipe( | |
* map(n => { | |
* if (n === 4) { | |
* throw 'four!'; | |
* } | |
* return n; | |
* }), | |
* catchError(err => of('I', 'II', 'III', 'IV', 'V')) | |
* ) | |
* .subscribe(x => console.log(x)); | |
* // 1, 2, 3, I, II, III, IV, V | |
* ``` | |
* | |
* Retry the caught source Observable again in case of error, similar to `retry()` operator | |
* | |
* ```ts | |
* import { of, map, catchError, take } from 'rxjs'; | |
* | |
* of(1, 2, 3, 4, 5) | |
* .pipe( | |
* map(n => { | |
* if (n === 4) { | |
* throw 'four!'; | |
* } | |
* return n; | |
* }), | |
* catchError((err, caught) => caught), | |
* take(30) | |
* ) | |
* .subscribe(x => console.log(x)); | |
* // 1, 2, 3, 1, 2, 3, ... | |
* ``` | |
* | |
* Throw a new error when the source Observable throws an error | |
* | |
* ```ts | |
* import { of, map, catchError } from 'rxjs'; | |
* | |
* of(1, 2, 3, 4, 5) | |
* .pipe( | |
* map(n => { | |
* if (n === 4) { | |
* throw 'four!'; | |
* } | |
* return n; | |
* }), | |
* catchError(err => { | |
* throw 'error in source. Details: ' + err; | |
* }) | |
* ) | |
* .subscribe({ | |
* next: x => console.log(x), | |
* error: err => console.log(err) | |
* }); | |
* // 1, 2, 3, error in source. Details: four! | |
* ``` | |
* | |
* @see {@link onErrorResumeNext} | |
* @see {@link repeat} | |
* @see {@link repeatWhen} | |
* @see {@link retry } | |
* @see {@link retryWhen} | |
* | |
* @param {function} selector a function that takes as arguments `err`, which is the error, and `caught`, which | |
* is the source observable, in case you'd like to "retry" that observable by returning it again. Whatever observable | |
* is returned by the `selector` will be used to continue the observable chain. | |
* @return A function that returns an Observable that originates from either | |
* the source or the Observable returned by the `selector` function. | |
*/ | |
export function catchError<T, O extends ObservableInput<any>>( | |
selector: (err: any, caught: Observable<T>) => O | |
): OperatorFunction<T, T | ObservedValueOf<O>> { | |
return operate((source, subscriber) => { | |
let innerSub: Subscription | null = null; | |
let syncUnsub = false; | |
let handledResult: Observable<ObservedValueOf<O>>; | |
innerSub = source.subscribe( | |
createOperatorSubscriber(subscriber, undefined, undefined, (err) => { | |
handledResult = innerFrom(selector(err, catchError(selector)(source))); | |
if (innerSub) { | |
innerSub.unsubscribe(); | |
innerSub = null; | |
handledResult.subscribe(subscriber); | |
} else { | |
// We don't have an innerSub yet, that means the error was synchronous | |
// because the subscribe call hasn't returned yet. | |
syncUnsub = true; | |
} | |
}) | |
); | |
if (syncUnsub) { | |
// We have a synchronous error, we need to make sure to | |
// finalize right away. This ensures that callbacks in the `finalize` operator are called | |
// at the right time, and that finalization occurs at the expected | |
// time between the source error and the subscription to the | |
// next observable. | |
innerSub.unsubscribe(); | |
innerSub = null; | |
handledResult!.subscribe(subscriber); | |
} | |
}); | |
} | |