1

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();
Rogerborg
  • 143
  • 1
  • 7
  • As I understand you want to perform and complete some operation before browser is closed. Have you tried using a modal confirmation or Javascript confirmation and perform the close yourself after the HTTP DELETE returns ? – bhantol Dec 08 '16 at 14:37
  • 1
    Hmm...I've seen two ways to get around this that don't get the NEVER BLOCK wrist slap, like the async:false thing: navigator.sendBeacon "This method addresses the needs of analytics and diagnostics code that typically attempts to send data to a web server prior to the unloading of the document. " https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon The other is use WebSockets. A server can tell when a socket has been disconnected, so you can do your unlock server side. I've actually used the WebSockets one a few different ways effectively. – Tim Consolazio Dec 08 '16 at 14:43
  • Possible duplicate of [JS onunload event won't always work](http://stackoverflow.com/questions/4307291/js-onunload-event-wont-always-work) – Heretic Monkey Dec 08 '16 at 15:03

2 Answers2

1

$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.

Scott
  • 860
  • 8
  • 10
  • 1
    Also from that same page: *Note: Starting with Gecko 30.0 (Firefox 30.0 / Thunderbird 30.0 / SeaMonkey 2.27), synchronous requests on the main thread have been deprecated due to the negative effects to the user experience.* Using deprecated functionality is probably not a good idea long term... – Heretic Monkey Dec 08 '16 at 15:02
  • Bingo, yes, that does still work. Mike is correct that it's noisily deprecated, but this is one of the few occasions where I really do need to do it. I'd initially though it wasn't working, but I just needed to pass my X-XSRF-TOKEN and then all was well (apart from the shouty deprecation warning). – Rogerborg Dec 09 '16 at 09:14
0

Recently, Have faced same issue while doing this kinda activity and resolved it by using navigator.sendBeacon() .The navigator.sendBeacon() method asynchronously sends a small amount of data over HTTP to a web server. For latest browser you could do like

window.addEventListener('beforeunload', function (event) {
    data = new FormData();
    // for CSRF Token
    token = $('meta[name="csrf-token"]').attr('content');
    data.append("key", value);
    data.append("authenticity_token", token);
  
    navigator.sendBeacon("URL", data);
    
    // Cancel the event as stated by the standard.
    event.preventDefault();
    // Chrome requires returnValue to be set.
    event.returnValue = 'Are you sure you want to leave this page without saving?';
  });

For more details checkout Navigator.sendBeacon() and Window: beforeunload event

Mayur Shah
  • 3,344
  • 1
  • 22
  • 41