Skip to content
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

Allow using startWith/from/of inside epics. #254

Closed

Conversation

franekp
Copy link

@franekp franekp commented May 30, 2017

Closes #253

@franekp franekp changed the title fix(createEpicMiddleware): Actions emitted with startWith/of/from are… Allow using startWith/from/of inside epics. May 30, 2017
@@ -45,6 +48,7 @@ export function createEpicMiddleware(epic, options = defaultOptions) {
return output$;
})
::switchMap(output$ => options.adapter.output(output$))
::mergeMap(action => of(action, asap))
Copy link
Member

@jayphelps jayphelps May 31, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm I'm pretty reluctant to schedule every action asap. Seems like it could create hard to understand behavior for people, as well as more complex stack traces--separately, I think ::observeOn(asap) would be the more idiomatic way of achieving asap scheduling, though they both would have the same net result seemingly. Don't worry about changing it just yet, need to figure out out if this is the best solution in general.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried using ::observeOn(asap) first, but it broke the it('should throw if you provide a root epic that doesn\'t return anything)' since exception was thrown from the asap scheduler and not from createStore(..., applyMiddleware(...)) call.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh right cause observeOn will schedule everything whereas mergeMap+of(a, asap) will just schedule next, not error/complete

@jayphelps
Copy link
Member

jayphelps commented May 31, 2017

Will need to think about this one some more, but my gut thinks the solution is to somehow wait until after the middleware is finished setting up before subscribing to the root epic. That's tricky because of the way the redux middleware API is designed as well as we want to be careful not to miss any actions dispatched synchronously outside of redux-observable (e.g. in server rendering cases this is common)

I'm pretty reluctant to schedule all actions on asap scheduler cause that feels like it might cause other problems and make the normal flow harder to reason for people--it also wouldn't be as performant but I'm not as worried about that.

btw this is definitely a bug. Your use case definitely should be supported, so thank you for bringing it up! I thought I had tested that, but seems I did not (didn't test the recursive nature)

for (const _ of new Array(10)) { // eslint-disable-line no-unused-vars
await (new Promise(setTimeout));
}
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use Observables to declaratively add these delays?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I will add change it.

@franekp
Copy link
Author

franekp commented Jun 1, 2017

the solution is to somehow wait until after the middleware is finished setting up before subscribing to the root epic.

Hmm, it turns out that there are 2 problems here:

  1. Actions dispatched during middleware initialization do not reach middleware
    Dispatching in a middleware before applyMiddleware completes reduxjs/redux#1240 - seems to not going to be fixed on redux side
  2. "race condition" between epic$.next(...) and synchronous actions emitted by epic:
    my guess is that somehow these actions are emitted before epic$.next(...) wires
    action$ to the input of new epic.

Waiting until after middleware is initialized solves (1) but not (2).

@BerkeleyTrue
Copy link
Collaborator

@franekp I'd wait to make any changes to this PR.

The most likely scenario is we move to a store enhance in place of a middleware. This will give us the flexibility to manage some edge cases that have popped up as well as add some power features not available to use using middleware API.

See: #256

@franekp
Copy link
Author

franekp commented Jun 1, 2017

I'd wait to make any changes

Hmm, too late.

@jayphelps Fixed without changing schedulers now, please review.

@mpiroc
Copy link

mpiroc commented Aug 26, 2017

Any update on this? I think it may be the root cause of a tricky bug I'm struggling with in my app.

@jayphelps
Copy link
Member

@mpiroc Should be fairly easy to verify--check if you are emitting an action immediately on app start via startWith or concat, and another epic is listening for this action but not receiving it but your reducers are. This is a fairly odd pattern IMO, but I don't doubt there are some rare use cases for it.

We're switching from a middleware to a store enhancer in #256. This is progressing, but admittedly slow because this is all volunteer work.

@mpiroc
Copy link

mpiroc commented Aug 28, 2017

Thanks @jayphelps, my bug turned out to be unrelated to this issue.

@jayphelps
Copy link
Member

@franekp I feel pretty bad that that is a pretty impressive PR, but has sat unmerged for so long. I'm sorry! It's still a use case I think we should support, I just don't have confidence yet this approach is the way to achieve it. I'm going to try to figure out #256 in the coming weeks, then we can circle back on this.

@franekp
Copy link
Author

franekp commented Oct 2, 2017

No problem, I understand. I am glad you want to support this use case.

jayphelps added a commit that referenced this pull request Apr 18, 2018
…s now supported.

fixes #253
closes #254, #259, #256

BREAKING CHANGE:

To support synchronously emitting actions when an Epic starts up, along with other fairly complex recursive action use cases, your root Epic is now subscribed to using the Queue Scheduler, and then any actions you emit are also scheduled using the Queue Scheduler. It's possible that some of your Epics could be relying on the fact that an action was historically emitted without Queue scheduling, most likely unknowingly. If none of your Epics rely on the actions emitted by another Epic or itself, you should see no changes.

As this is a complex topic to elaborate on, and the Queue Scheduler is not well known, you'll want to learn more here: https://redux-observable.js.org/MIGRATION.html
@jayphelps
Copy link
Member

I believe this was fixed last year. Definitely lmk if not! 62a8c73

@jayphelps jayphelps closed this Nov 15, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Actions emited with Observable.{startWith,of,from} are swallowed silently.
4 participants