0

I have a web service with a method that takes quite a long time to complete. So I start the ajax client. It sends the request and it waits. Everything is fine when the request completes.

But when I use reload / refresh option in my browser (let's say Chrome) - all hell breaks loose. The pending request is shown in new page as still pending. The new pending request is made, but the old one still sits there. The new request lag or cease to respond.

A server fault? Not really, because all works fine when I restart the browser itself.

BTW, on latest Firefox my ajax app crashes when I reload the page during ajax fetch. I mean it hangs on first ajax request. Of course restarting the browser helps.

Here's configuration: My service is a WCF service running on localhost. The service responds to GET requests, however they must be authenticated with API key sent with a cookie. The service returns mostly JSON responses.

The communication uses CORS, no problems there.

I test my client using MiniWeb server. The application is pure ECMAScript, no framework or dependencies.

Here's my ajaxGet method:

/**
 * Performs a GET request to the WCF, returns response data.
 * @param {any} path Request path without domain and service path.
 * @returns {object} Response data.
 */
async ajaxGet(path) {
    let isFirstCookieSet = false;
    if (this.serviceDomain === undefined) this.parseBaseUrl();
    Cookie.set({ "api-key": this.apiKey }, { "domain": this.serviceDomain, "path": this.servicePath });
    let response = null;
    try {
        response = await fetch(this.baseUrl + path, {
            "method": "get",
            "mode": "cors",
            "credentials": "include"
        });
    } catch (error) {
        console.info("Caught error during fetch operation before getting response body.");
        throw new BackendError(404, "Service not available");
    }
    if (response.status !== 200) throw new BackendError(response.status, response.statusText);
    let container = await response.json();
    let data;
    for (let k in container) if (container.hasOwnProperty(k)) {
        data = container[k];
        break;
    }
    Cookie.set({ "base-url": this.baseUrl, "api-key": this.apiKey }, { "expires": DateTime.now.addYears(1).value, "samedomain": true });
    return data;
}

Well, it depends on my other code (obviously the App class the method is a part of, DateTime and BackendError classes), but it's irrelevant here.

How to prevent this weird, undefined behavior? Are there any special fetch options? Should I set a specific request header? Should I set a specific response header in my web service? Any other magic?

UPDATE: I found I can abort the pending request manually:

How do I cancel an HTTP fetch() request?

But it would work if I could send a signal when browser reload is made, and AFAIK there's no way to execute any script when it happens. Or is it possible?

Harry
  • 4,524
  • 4
  • 42
  • 81
  • I haven't got a solution directly, but is there any indication to the user that shows something is happening? – Jordan Quartermain Jul 23 '18 at 05:20
  • 1
    Can you please show us the form tag's output from devtool which you are using? – Ullas Hunka Jul 23 '18 at 05:20
  • @UllasHunka : I use Chrome dev tools. I don't know how to dump network request list. There is 1 request pending when the app is first started. After page reload there are 2, after 10 reloads there are 10. The last one is 200 after a while (exactly when the current request completes), but the rest of the pending requests never complete. Restarting the service does nothing to them, restarting the browser (or opening the app in a new tab) works fine. – Harry Jul 23 '18 at 05:48
  • @JordanQuartermain : On Chrome and Chromium based browsers: hardly anything. The app works, however lags a little after one reload. Lags more and more on each reload. Then the browser crashes, just stops to handle any new ajax requests. Just waits, the app hangs. When the app is opened in new page - it works without any lagging. – Harry Jul 23 '18 at 05:52
  • Actually, my mean was just to view your HTML. Are there any possibilities that we can view your HTML? – Ullas Hunka Jul 23 '18 at 06:06
  • @UllasHunka : The HTML is irrelevant here. The request is made from JS, the HTML originally contains only head and 1 div, everything is done with ajax, like the templates are loaded and processed with the data read from WS. All works, the problem is exactly with dropped ajax requests. They seem to remain in pending status after reload occurs. Then every subsequent ajax request to the same URL lags. Then the browser tab becomes broken. Like the browser tab remebers all pending requests and never clears them. – Harry Jul 23 '18 at 06:13

1 Answers1

1

Long story short: we need AbortController and window.onbeforeunload.

let abortController = new AbortController();
window.onbeforeunload = function(e) { abortController.abort(); };
let myResponse = await fetch(uri, { /* my other options */, "signal" : abortController.signal });

What happens here? Browser allows some cleanup operations to be performed when page refresh is requested (or probably the tab closed or whatnot). This sits in window.beforeunload event handler. Then my fetch() call gets optional CancelationToken object, it's the signal from my AbortController instance. When the controller is triggered, all tokens receive the abort signal and get canceled.

MDN says this thingie is experimental. Anyway, it works fine with Firefox, Chrome and Opera.

Harry
  • 4,524
  • 4
  • 42
  • 81