Skip to content

Handling failed single-page app navs #47

Closed
@domenic

Description

@domenic

The design of navigation interception, especially after #46 lands, leads to a novel situation. Namely, you can perform a single-page app navigation which fails, by passing a rejected promise to event.respondWith(). A somewhat-realistic example might be:

appHistory.addEventListener("navigate", e => {
  e.respondWith((async () => {
    const response = await fetch("https://nonexistant.invalid/some-data.json");
    // ... etc ...
  })());
});

// This will get intercepted and converted to a failure.
location.href = "/foo";

The current plan as of #46 is that, because of the issues discussed in #19, all such navigations will synchronously succeed, but then asynchronously fail. Roughly:

  1. location.href, the URL bar, etc. immediately start pointing to /foo, as if you'd done history.pushState(null, null, "/foo").
  2. appHistory.current, as well as the underlying session history model, gets pointed to a new history entry for /foo.
  3. Any forward history entries get thrown away.
  4. But then, once the browser sees that the promise is rejected, it "rolls back" the navigation:
    1. It sets location.href, the URL bar, etc. back to the original URL that initiated the navigation.
    2. appHistory.current, as well as the previous session history model, navigates back to the previous session history entry.
    3. The history entry for /foo created in step (2) gets thrown away.
    4. The forward history entries thrown away in (3) remain dead.

Is this the right model for when the navigate handler tells us the navigation failed?

The main alternative, I think, is to do a lot less. I.e., do not try to provide a rollback; leave location.href, the URL bar, etc. on /foo. Instead just signal to the app (e.g. with an event on window.appHistory and/or window.appHistory.current) that the promise rejected, and have the app handle that. The app could do its own rollback, perhaps. Or it could display a literal error page, like it might for a server-side 404.

I'm kind of leaning toward this do-less model. What do people think?

The other alternative would be to go the route of (2) in #19 (comment), which would allow us to delay all the updating stuff until we're sure the promise fulfills. Then there would be no navigate-then-rollback; we'd just never move at all in a failure case.

Metadata

Metadata

Assignees

No one assigned

    Labels

    changeA proposed change to the API, not foundational, but deeper than the surface

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions