-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
catchError does not wait for the target observable to complete #5115
Comments
I think the problem is rather subtle. It's because const observable$ = new Observable(observer => {
observer.error("error thrown");
// observer.complete();
}); So it's not the Essentially, the error is being caught - and dealt with - but the source then completes. I guess this could be deemed a bug, though. Once the source emits an error, that should be the end of things as far as the source's subscribers are concerned. They should not be receiving further notifications from the source. |
I was investigating this, and the const syncSource = new Observable(subscriber => {
subscriber.error('err');
subscriber.next(1);
subscriber.complete();
});
const asyncSource = new Observable(subscriber => {
Promise.resolve().then(() => {
subscriber.error('err');
subscriber.next(1);
subscriber.complete();
});
});
syncSource.pipe(retry(2)).subscribe(console.log, console.error, () => console.info('complete'));
// logs 'err' to console.error
asyncSource.pipe(retry(2)).subscribe(console.log, console.error, () => console.info('complete'));
// logs '1' to console.log, then 'complete' via console.info In short, every operator that recycles the subscriber (i.e. doesn't set |
I'm no longer sure that this is a bug. If you read the Observable Contract, you will find this:
I think that the source in this example is behaving in a manner that is contrary to the contract and the unexpected behaviour should not be surprising. I would never call |
I agree with @cartant here, error / complete is both terminal condition and observable should have one condition only. Pretty much same as resolve / reject, you can't resolve after reject. |
In promises the following code only resolves, ignoring the call to reject. console.log(await new Promise((resolve, reject) => {
resolve('hello world');
reject('bye cruel world');
})); In rxjs's observables the same happens: The existence of |
@bgotink I'm wary that this might affect anything that's using an Basically, this appears to be a gap in the defensive programming to which you have referred. I'm still not inclined to call this a bug. It's entirely possible that this might not be solvable without some fundamental architectural changes. ATM, I'm more inclined to write a linting rule to catch (at least some of) these types of non-compliant sources. |
The linting rule is a good idea.... however, a dead subscriber is supposed to stay dead. It's a violation of our contract, and I'm inclined to call this a bug. Interestingly, in this case, this wasn't an issue most of the time back when it was created in v5, because in v5 we synchronously threw the error if it was unhandled, which means that the function block with the superfluous That said, this is an edge case bug, because people that are hitting this are doing bad things.. It's been 5 years since this could have happened, and 2 since we stopped sync error rethrowing, and this is the first time I've seen this. So we should prioritize accordingly. This is likely something we can resolve when we get to a more comprehensive rewrite in v8. |
Regarding linting rules, there is:
|
i suggest every event from source observable emit can represent as a fully wraper object: let yieldEventPayload = {
kind: "next" | "complete" | "error",
value: any,
done: false | true,
error: null | new Error(),
} it compatible |
…us source error handling - Refactors catchError to be simpler and to get rid of _unsubscribeAndRecycle usage as well as some other overly clever bits. fixes ReactiveX#5115
Bug Report
Current Behavior
catchError returns the data emitted only by atomic observables, who return the value immediately, rather that waiting for the target observable to complete.
Reproduction
This does not represent the flow on which I had the issue is just a simplification of the problem.
The result is :
rather than
Expected behavior
catchError should act as a switchMap in case of error.
Environment
By the look of the documentation from https://rxjs-dev.firebaseapp.com/api/operators/catchError this looks like a bug because catchError can be used as a retry.
The text was updated successfully, but these errors were encountered: