javascriptangularjsonunload

Javascript / angular: perform asynchronous http from onunload


I'm working with a web app that locks resources on a server. To unlock them, it has to delete a resource on the server with an HTTP DELETE. I know this isn't reliable and there's a periodic cleanup running as well which unlocks them, but the goal is to unlock the resource as soon as possible.

I cannot change the locking architecture (it's not my system), I just have to make the best stab at unlocking.

One point where I need to unlock is when the tab or browser is closed. First, I'm handling the onbeforeunload and if the document is dirty, prompting the user for confirmation that they want to close:

$window.onbeforeunload = function() {
    if (documentIsDirty) {
        return "Some prompt text";
    }
};

I can't unlock from within onbeforeunload, as the user may choose to cancel the close. But there's no event (correct me if I'm wrong) between onbeforeunload and onunload.

If I try to make the call from in onunload, then the tab/session gets destroyed as soon as the onunload function returns. Trouble is, that's before the http request has completed, and it turns out that the resource doesn't actually get unlocked.

$window.onunload = function() {
    $http.delete('/a/lock/url');

    // I've tried forcing a digest cycle to try and flush the request
    // through, but it never gets sent either way
    $rootScope.$digest();
};

Now, I know it's anathema to actually block in Javascript, but it appears that once onload returns, that's all she wrote.

Is there any way to block until the http request has actually completed, and prevent onunload from returning until then?

[UPDATE] Solution was as below - use XMLHttpRequest synchronously. It's noisily deprecated but does (at the time of writing) still work at least in Chrome.

var request = new XMLHttpRequest();
request.open('DELETE', url, false);
request.setRequestHeader('X-XSRF-TOKEN', myXSRFToken);
request.send();

Solution

  • $http will always do requests asynchronously because internally it's just using XMLHttpRequest and always passing true as the third parameter to a request's open function. From the MDN documentation for XMLHttpRequest's open function:

    An optional Boolean parameter, defaulting to true, indicating whether or not to perform the operation asynchronously. If this value is false, the send()method does not return until the response is received.

    If you want to do a synchronous request you can just use XMLHttpRequest directly and pass false as the third parameter to open. Since this is when the site is closing it's not really necessary to use Angular's $http anyway.