-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Subscription to an observable should probably be async #597
Comments
EDIT: I changed the example above to be some async source. The first example wasn't quite what I meant. |
Looks like a reasonable way to fix a thorny issue without introducing performance penalty for hot operations. |
Actually, you are talking about "async observables", which is a new world duality of "sync observables" (which the |
Yeah. Async has different implications in JavaScript though. It's not "concurrent" async, it's more "push this to the next tick" sort of async. |
+1 The important thing is removing non-determinism around whether or not side-effects may occur in the current call stack. It protects consistency in the current stack much like |
+1 for the reasons that @briancavalier pointed out |
I've actually changed my mind on this a bit. The thing is, reactive streams programming is all about controlling side effects. The only place you should be creating side effects is in your subscription. RxJS has to default to this "sync if it can be" sort of behavior for performance reasons, otherwise every operator in your chain would incur another frame or tick. merge and flatMap exacerbate this. RxJS gives you the option of using |
What I'm talking about: the actual
.subscribe
or.forEach
call.What I'm not talking about: side effects that occur after or during that call.
The problem
As it currently stands, if I were to give you an arbitrary Observable instance, you would have no way of knowing how a call to
subscribe
might effect the surrounding code block. This is because the actual call to subscribe itself is synchronous, as are the calls toonNext
within it. However, thoseonNext
calls may, or may not, be wrapped in asynchronous code. So you're not guaranteed that side effects will not affect your currently executing code block.For example, given these two sources:
The following code will behave differently:
In the example, dealing with console.log is obviously fairly safe and innocuous, but developers might be doing other things such as incrementing a value or what have you. Things things might pass all tests and even run in production just fine because they're relying on the observable behavior being synchronous or asynchronous, but one day the observable being passed in switches, or worse, alternates between the two, and the developer is face to face with a heisenbug.
Proposed Solution
Simply make the act of subscription to Observables asynchronous. Essentially, this would be a
subscribeOn(Scheduler.timeout)
by default.If someone wants the immediate behavior for subscription, they can still do it explicitly with
subscribeOn
.Risks
This is clearly a breaking change. The only upside being that it should only break code that is probably poorly written to begin with.
Other considerations
It appears, to me at least, that @jhusain's async generator proposal for ES7 is going to make subscription behave asynchronously as above. I'm not entirely sure about that so I've submitted an issue there to ask, and hopefully strike up public conversation on the matter. I think (but I'm not sure) other libraries such as Bacon and Most.js have this behavior. It seems like something that should be standardized (ala Promises and A+).
The text was updated successfully, but these errors were encountered: