-
-
Notifications
You must be signed in to change notification settings - Fork 422
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
Order of sinks can still be an issue #512
Comments
I reproduced it here, also happens with the latest (non-rc) versions of the dependencies you mentioned. Seems like a legit bug. I'm labeling it scope:dom, scope:http, scope:run because it's unclear so far where the source of the problem is. Any ideas @whitecolor? You helped us solve this in |
Seems like the scope of the problem is Onionify, because replacing reducerMimic$.imitate(sinks[name]); with var buffer = {next: [], error: [], complete: []};
var replicator = {
next: function (x) { buffer.next.push(x); },
error: function (x) { buffer.error.push(x); },
complete: function (x) { buffer.complete.push(x); },
};
sinks[name].addListener({
next: function (x) { replicator.next(x); },
error: function (x) { replicator.error(x); },
complete: function (x) { replicator.complete(x); },
});
buffer.next.forEach(function (x) { reducerMimic$._n(x); });
buffer.error.forEach(function (e) { reducerMimic$._e(e); });
buffer.complete.forEach(function () { reducerMimic$._c(); });
replicator.next = function (x) { reducerMimic$._n(x); };
replicator.error = function (e) { reducerMimic$._e(e); };
replicator.complete = function () { reducerMimic$._c(); }; Solves the problem, but I still want to know exactly why, deeper. |
I believe the same problem as it was in cycle core itself: late listeners of |
Closing this issue here because we opened an issue in onionify staltz/cycle-onionify#17 |
@staltz I can reproduce this without onionify. Looks like it's something to do with using
Might be a bug with xstream even? |
@binary-koan I believe this is by design. xstream is multicast by default, meaning it's not referentially transparent. Not referentially transparent means the first use of The reason the non-merged stream does work as you expect is because it's not merged with a hot (will-not-complete) stream. So the second subscription sees the completed stream, and thus does trigger a subscription which causes the sync emissions the second time around as well. I've actually never seen the issue this example exposes in a real-world project because the need for If you change your BTW, I'm saving this question. It's perfect for a streams-in-JS interview. It's got all the topics which make streams hard to initially grok... hot/cold, unicast/multicast and event-loop-ordering (sync/async) 💯 😉 |
@pixeleet Regarding your original question, the HTTP sink emission in the child is synchronous, so it happens before the response stream has been subscribed. One hacky solution at that time was to add a small delay the sync HTTP emission to the sink so the source subscription completes before the emission, but this was more generally solved by the microtask queue update: 9d0fc02 |
This is actually a problem in @binary-koan's app, and using Even if this is by design, I think we should question the pros and cons of that design. This is quite unintuitive and can cause nasty bugs that are hard to resolve. One option I can see to resolve it is to actually make Since |
Thanks @binary-koan for the reproducible snippet. I read this issue but could not find time until now to comment in details. I still want to find the root cause instead of putting a simple setTimeout or setImmediate there. @whitecolor is strongly against that approach, and I nowadays tend to agree with him (after having put setTimeout inside cycle/run and seeing some non-intended problems). It could be an issue related to scheduling: Or could be just a detail or corner case in the buffer system in cycle/run, or something along those lines. I'm careful to not go to a quick solution for this, I want to first understand what's the root cause and how broad is it as a foundational issue. In summary, I "agree with everyone" here, just want to make sure we move carefully and very intentionally with surgical precision. |
Thanks for all the explanations and context - great to get more of an idea of what the issues and concepts surrounding this are! I'll just add a thought as someone relatively new to building apps in cycle: one of the great realisations I had was how I could set up all my game interactions and how they map to states up front, then just leave cycle to schedule all those interactions in a way that makes sense. It was oddly jarring to be taken out of that convenient asynchronous world and back into one where the order of method calls matters and state is something you have to worry about, just because (as I imagined) I happened to be firing an action at the start of the timeline. I'll definitely be reading up more on different implementations of streams and when they're useful, just worth noting that (for better or worse) this was the first time I even needed to care about concepts like sync/async and multicasting in a couple of weeks of tinkering with cycle. |
Okay, in the big picture, I can now realize that @binary-koan's example was about scheduling, which expressed itself through the ordering of sinks, but that's just because the ordering of sinks is a one-to-one relation to the order of subscribes underneath. This original issue was about race conditions when during initial setup, and I know these two cases sound very similar, but scheduling is more general than this issue. So I'll close this issue. You can always open another issue that refers to this issue, but the truth is that this issue is resolved. Furthermore, the latest example was about scheduling with xstream, so it should actually belong to the xstream repo. If you use RxJS v5+ and opt-in to use a breadth-first scheduler (like That said, I agree with @Widdershin that we should focus on intuitive APIs, and using a breadth-first scheduler by default would "just work" for most people. We would then need to figure out: (1) how to keep the stack trace sane for those wishing to debug their app OR provide completely custom debugging tools, and (2) how to document/teach when to opt-in to faster schedulers. So this is something I want to eventually fix, but it's a big project. |
Sorry, for the kind of bloated repro-code couldn't make it smaller :( I have a hunch that the issue is somewhat inherent to the HTTP driver but I can't put my finger on it yet.
Code to reproduce the issue:
Expected behavior:
First I see the
Posts
component, rendering the titles of the 100 postsAfter 5 seconds I see the
Users
component which renders the 10 usersActual behavior:
First I see the
Posts
component, rendering the titles of the 100 postsAfter 5 seconds I see the
Users
component but it doesn't render the 10 users, the request is fired but it's response handler is never called.Versions of packages used:
The text was updated successfully, but these errors were encountered: