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

Feature Request: Ability to access fetch errors when the network or server is unreachable during GET requests #460

Open
corporealfunk opened this issue Nov 21, 2021 · 7 comments

Comments

@corporealfunk
Copy link

In the case where a fetch request fails because the network or remote server is unreachable, a NetworkError is thrown and logged in the console (as well as the uncaught in promise error). If the fetch request was created because of a form POST, turbo:submit-end is also dispatched and developers can access the submit state and error message via the dispatched event.

However, other actions that may cause Turbo to create a fetch GET request that then fails because the network is unreachable (for example, programmatically setting a turbo frame src attribute, or using a form with GET instead of POST) do not dispatch any turbo:* events at all and developers do not have the ability to programmatically respond to the network error.

Could these types of failures always dispatch a turbo event, not just in the case of a form submission?

@seanpdoyle
Copy link
Contributor

As of #424, <form method="get"> submissions will fire a turbo:submit-start and turbo:submit-end event. The turbo:submit-end has HTTP response information in the event.detail.

Outside of that, would attaching listeners for turbo:before-fetch-request events suit your needs?

@corporealfunk
Copy link
Author

#424 looks like it will work for my use case.

turbo:before-fetch-request dispatches an event that only contains the fetchOptions regarding what is about to be fetched, and happens too early in the fetch lifecycle to contain any information about a network failure or unavailable server.

@seanpdoyle
Copy link
Contributor

@corporealfunk you're correct in that. What I meant to share was the turbo:before-fetch-response event that's a companion to the request. That should fire for a Visit, a Form Submission, and event a Frame navigation.

@tleish
Copy link
Contributor

tleish commented Nov 24, 2021

@seanpdoyle - I think turbo:before-fetch-response is the appropriate location, as long as the listener can call event.preventDefault() to stop the application from rendering an error page so you can show a custom message.

As of right now, this does not fire on a network connection is lost for Visit using the following steps:

  • Add Turbo event listener for turbo:before-fetch-response
  • Load page with turbo
  • Chrome > Developer Tools > Network Tab > Change Throttling to "Offline"
  • Click on link

turbo:before-fetch-response is not called.

FYI, similar request in #460

@corporealfunk
Copy link
Author

corporealfunk commented Dec 15, 2021

I'm actually going to reopen this. My previous use case was satisfied with the merge of PR #424, but I have another use case now where Turbo doesn't seem to expose fetch error states like NetworkError:

If I set a turbo frame's "src" attribute programmatically and the loading of that URL results in a NetworkError, there seems to be no way to intercept that error. #424 only deals with form submissions.

Note that setting a turbo frame's src does result in some turbo events getting triggered (for example turbo:before-fetch-response does get fired when a response was returned from the server).

It'd be great to have a more global way of accessing any turbo-driven Fetch request that results in a NetworkError, not just for form submissions. We have them for Forms with turbo:submit-start (we should be able to catch NetworkErrors here in the detal.formSubmission.state).

@corporealfunk corporealfunk reopened this Dec 15, 2021
@seanpdoyle
Copy link
Contributor

@corporealfunk I'm curious, if you set frame.src = "..." programmatically, does subsequently calling await frame.loaded throw an Error in the case of a network failure? Does the Promise return anything?

@corporealfunk
Copy link
Author

If I deal with the frame.loaded promise like so, the NetworkError does not get caught, in fact the promise still resolves! And we'll see why in a minute...

turboFrame.src = newLocation;
turboFrame.loaded.then(() => {
  console.log('good load', arguments);
}).catch(() => {
  console.log('bad load', arguments);
});

I'm not super familiar with Turbo internals, but here, we can see that the code that makes the fetchRequest creates a Promise that only calls resolve and never rejects:

return new Promise<void>(resolve => {

And here, frame.loaded is assigned to the promise returned above:

this.element.loaded = this.visit(expandURL(this.sourceURL))

Tracing further, the FetchRequest's perform method is called:

async perform(): Promise<FetchResponse | void> {

When an error is caught in the promise chain, the delegate's requestErrored method is called:

this.delegate.requestErrored(this, error)

and the error is re-thrown. In addition, the delegate's requestErrored method is called... and that method console logs the error, and then does this:

this.resolveVisitPromise()

So, it seems to resolve the promise even on error.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

3 participants