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

Don't go back to oldUrl when navigation occurs during slow async cancelation. Resolves #950 #969

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

joeldenning
Copy link
Member

See #950. I'm not sure if this is the best implementation approach. Am open to other ideas. Ignoring a canceled navigation is something that should happen when the url changed while we were waiting for the cancelation promise to resolve.

@Aaron-Pool
Copy link

Aaron-Pool commented Mar 30, 2022

@joeldenning This problem is slightly more pervasive than just async cancellation. The more overarching issue appears to be that the time between when single-spa's patched pushState handler is called and when the before-routing-event and subsequent reroute starts to happen is non-neglible (in my experience, it's approximately ~100-150ms which allows plenty of time for another navigation to be triggered (particularly a programmatic navigation) and then be essentially discarded as single-spa's eventual artificial PopState pre-empts the new navigation.

Here's an order of events I recently captured in my app, in which I was experiencing an unexpected cancellation.

  • 0ms -> Vue routes to a parent component at /parent
  • 0-9ms -> History.pushState is called, single spa intercepts
  • 9ms -> Vue router finishes routing to parent component
  • 58ms -> Parent component uses state data to decide whether to send the user to child route parent/a or parent/b, data indicates parent/a is the appropriate choice, VueRouter begins running through the "before" navigation guards for parent/a
  • 162ms -> single-spa:before-routing-event handing begins for the first routing event
  • 184ms -> single-spa triggers artificial popState event
  • 208ms -> single-spa:routing-event is emitted
  • 209ms -> Vue router sees the popState and triggers a navigation to the corresponding /parent route, immediately cancelling the navigation to parent/a while it is going through it's pre-pushState navigation guards

Final url end as /parent, with no child state navigation happening at all.

Can we expose some sort of api/event the user can use/emit to communicate that a pending navigation has been pushed on to the stack, and the existing reroute should be invalidated in favor of a routing which is about to occur. It's not that the first routing was cancelled, but rather that it has been preempted by a new, immediate navigation, and Single-spa doing a popState will revert that new navigation.

@Aaron-Pool
Copy link

Aaron-Pool commented Mar 30, 2022

If user land had a corresponding set of events or api calls to communicate routing-initiated and routing-canceled events, they could (at least in the case of vue-router) be emitted in global beforeEach guards, then single-spa could delay a reroute until either:

  1. a routing-canceled fired (indicating the routing that was initiated was prevented during pre-routing guards) in which case the current reroute would resume, or
  2. the History.pushState actually happened, in which case the current reroute would be canceled in favor of the new one

@joeldenning
Copy link
Member Author

The complexities here make me think it was a mistake to implement navigation cancelation at all. I resisted the feature for about a year and finally gave into it, only to have constant problems with edge cases. Users have asked for mutually contradictory functionality with it.

I'm considering just closing this pull request as I don't have the time or desire to read through mountains of text (including in various related issues) to solve problems in a way that will only create more problems.

The more overarching issue appears to be that the time between when single-spa's patched pushState handler is called and when the before-routing-event

My guess is that this is happening due to single-spa only processing one navigation at a time, rather than concurrently handling multiple navigations. If pushState was not called in the middle of an already-running single-spa reroute(), then there would be very little time (just a couple microticks of the event loop) between pushState and single-spa:before-routing-event

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.

None yet

2 participants