-
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
If a hot observable's subscriber ignores errors, subsequent subscribers also ignore errors #2145
Comments
There's a bug in RxJS that breaks hot observables if one subscriber ignores errors (see ReactiveX/rxjs#2145 ), which is causing #308. This patch fixes it by always handling observable errors (handling errors is a good idea anyway). Fixes #308
Edit: this particular comment isn't applicable, skip to #2145 (comment) Actually, I'm not sure this is a bug. What actually seems to be happening is that, if you don't provide an error handler, the error bubbles and the last subscriber doesn't even run var observable = Rx.Observable.throw('some error').share();
// This subscriber cares about errors and gets them
observable.subscribe(function(res) {
console.log("first subscriber, next " + res);
}, function(err) {
console.log("first subscriber, error " + err);
});
// This subscriber does not care about errors
observable.subscribe(function(res) {
console.log("second subscriber, next " + res);
});
alert('!!! THIS CODE IS NEVER REACHED !!!');
// This subscriber cares about errors, but never gets them
// because it never actually runs!
observable.subscribe(function(res) {
console.log("third subscriber, next " + res);
}, function(err) {
console.log("third subscriber, error " + err);
}); This seems as-designed. If you don't catch the error, they propagate as normal exceptions would. |
This example shows the bug better:
Output:
Note that there is no |
@imgx64 Subscriber's default behavior is to throw error unhandled errors. The alternative is swallowing them, and nobody wants that. An Observable synchronously emitting an error to a Subscriber without an error handler behaves the same as synchronously calling a function that throws without wrapping in a try/catch. The issue you linked to is a discussion about whether we should catch errors thrown in the end Subscriber's Even if we did that though, unhandled errors would still end up interrupting the execution for Subscribers without an |
I understand about synchronous errors, and I've posted a second example that shows it applies for asynchronous errors.
The reason I mentioned that bug is this comment, which says that an unhandled error in one subscription terminates all other subscriptions to the same observable. This is what I'm seeing here which I think is unexpected behavior.
But this is interrupting the execution for subscribers with an error handler if a subscriber without an error handler happened to subscribe to the same observable. I tried the same thing with RxJava and got what I expected: Code:
Output:
|
Hey, we discussed this today at our meeting, notes here. We're going to keep the existing behavior for now (v4 had the same behavior), but TC39 is considering this exact problem in the context of DOM event streams, which are multicast as well. So we're going to wait and see what direction they want to go in. |
The TC39 discussion on this is here: tc39/proposal-observable#119 |
Has a final consensus been reached on this issue? If so what is it? Thanks |
@ccrowhurstram nope. any updates would be posted here. The tc39 thread (link above) the discussion was left at:
But this isn't the final word on it yet. It's too early in the spec process to have anything solidified and we don't want to change things in RxJS prematurely just to have to change them back a few months later. |
OK thanks for the information |
BTW, slightly off topic here but worth the cross reference... The work around to the problem that uses The error thrown by an operator like I've reported the issue with angular here: angular/angular#14316 |
For working around this issue with errors, if I wanted to "percolate" errors to all the subscribers of a subject, would I be best to create a "safe" subject as @benlesh's article pointed out was the issue? The sample workaround provided to use the I apologize if I over simplified anything, I'm not overly familiar with rxjs at this point. |
I had to write an interim function which checks if a subscriber has error callback missing, and adding an empty one in that case. Quite an ugly approach. |
@georgique the behavior in v5 is as intended, so this ticket could prolly be closed. In v6 errors from subscribers will not be rethrown, but they will still be reported to the console/window.onerror for debugging. That's very similar to the behavior of Promises. #3062 |
@jayphelps I might misunderstand things a bit, but initially issue was not about errors in subscribers, wasn't it? I have pretty simple scenario - an service sending login request to a server with wrong credentials. When a response is received, I have an Rx Subject broadcasting results to subscribers, so different system parts can react on login attempt. In my particular case I have three subscribers, two of them do not handle errors, it's just not needed there. The third though has to handle error, because it is the one showing error to the end user. But error never gets to the third subscriber just because first two don't have error handlers. If that's intended behavior, then maybe at least make error handlers compulsory? |
@georgique That's okay. Indeed it was about errors in subscribers. In v5 if you don't provide an error callback a default one is provided for you which just rethrows the error. In this context I mean "subscriber" to be the person who is subscribing with an observer that does not include an error callback. Here's a demo https://jsbin.com/ruhosup/edit?js,console,output
It's intended behavior for v1-v5, but won't be in v6. It won't be possible to change this or make error handlers required in v5 because it's a breaking change. If you want to mitigate it you could schedule the subscription to happen asynconronously using the I would use the AsapScheduler in this case IMO as the microtask will happen relatively faster. https://jsbin.com/lifehu/edit?js,console,output import { asap } from 'rxjs/schedulers/asap';
// when anyone subscribes it won't happen synchronously, instead
// each will be scheduled on their own microtask
const items = (new Subject()).subscribeOn(asap);
items.subscribe({
next: d => console.log('first ' + d),
error: e => console.error('FIRST SUBSCRIBER SAW IT')
});
items.subscribe({
next: d => console.log('second ' + d),
// no error handler so one is provided for you that
// will rethrow the error synchronously but the fact that we scheduled
// using the AsapScheduler means that rethrown error will not affect anyone else
// because it happens with its own fresh callstack.
});
items.subscribe({
next: d => console.log('third ' + d),
error: e => console.error('THIS IS ALSO CALLED, CORRECTLY')
});
items.next(1);
items.next(2);
items.error('bad stuff'); Keep in mind that this has potential negatives too, since your subscription doesn't happen synchronously it becomes harder to reason so could introduce bugs. Technically it doesn't perform as well relatively speaking but the difference is negligible in most cases since you're not typically subscribing thousands of times per second |
Now clear. Thanks @jayphelps. |
Apologies I'm late to this party. This is an issue called "Producer interference". It's been dealt with in the TC39 proposal, and it's also actually dealt with in Basically to fix this we have to break the following: try {
of(1).map(() => { throw new Error('lol') }).subscribe();
} catch (err) {
console.log('this code should definitely be hit in RxJS 5.5, but won't in Rx 6');
} The problem exists, as I'm sure some of you have figured out, because unhandled errors are immediately rethrown in RxJS. This means that they'll unwind the stack back to a loop that is notifying for a multicast, and break the loop. The solution is quite simple, and makes sense for consistencies sake: Just schedule the throw on a timeout. The truth is that putting a try/catch around a subscription is silly, and accommodating it doesn't make any sense. So we've moved away from that potentially buggy behavior, and it actually cleaned up a lot of code for us. FYI: The workaround for this is to use |
This was resolved with the release of v6 almost a year ago. Closing. |
If a subscriber subscribes to a hot observable with a
next
handler only (noerror
handler), then any subscribers that try to subscribe afterwards don't have theirnext
handler called.RxJS version:
rxjs@5.0.0-rc.4
Code to reproduce:
https://plnkr.co/edit/0CgdiUZ7iyIzzBKpoBTL?p=preview
Expected behavior:
The first and third subscriber's
error
handler should be called, and this output should be logged:Actual behavior:
Only the first subscriber's
error
handler is called, the third subscriber'serror
handler is not called. The below is logged instead:Additional information:
Possibly related: #1420
The text was updated successfully, but these errors were encountered: