Don't show JavaScript error flashes when AJAX calls are cancelled due to browser navigation
Summary
When navigating to a new page on GitLab.com, an error message is often shown on the current page right before navigation:
This is because an in-progress AJAX request has been cancelled due to the navigation, and the error handler interprets this cancellation as a failure and displays an error message.
Steps to reproduce
Note: this is easier to reproduce on a slow/throttled network connection.
- Navigate to a page that makes an AJAX request for additional data after load time; for example, a merge request's page.
- Right after the page's initial load, click the browser's back button.
If you timed your back button click correctly, you should see an error message displayed right before the new page is loaded.
What is the current bug behavior?
The error handler interprets all AJAX errors as failures and renders an error message in the UI.
What is the expected correct behavior?
The error handler ignores AJAX errors that are caused by browser navigation
Output of checks
This bug happens on GitLab.com.
Possible fixes
We often handle network errors by attaching a catch-all .catch
handler to our network request promises. For example: https://gitlab.com/gitlab-org/gitlab-ce/blob/13581fe06c1fb34351010f5f883d6263528438bd/app/assets/javascripts/gpg_badges.js#L34
const displayError = () => createFlash(__('An error occurred while loading commit signatures'));
return axios
.get(endpoint, { params })
.then(({ data }) => {
// handle success here
})
.catch(displayError);
We should make our error handling a little bit smarter by detecting if the failure was legitimate or if it was caused by browser navigation. This article includes one approach: How to Distinguish a User-Aborted AJAX Call from an Error.
tl;dr:
/**
* Returns true if the user hit Esc or navigated away from the
* current page before an AJAX call was done. (The response
* headers will be null or empty, depending on the browser.)
*
* NOTE: this function is only meaningful when called from
* inside an AJAX "error" callback!
*
* The 'xhr' param is an XMLHttpRequest instance.
*/
function userAborted(xhr) {
return !xhr.getAllResponseHeaders();
}
It would be best to encapsulate this logic in something that is reusable so that we don't have rewrite this code for every network request.
Unfortunately, we don't currently have a centralized way of handling network request errors; each request handles its own errors. This means fixing this issue will require updating every network request that shows flash error messages. It would be great to centralize this error handling to make changes like this easier in the future.