wait for promises in onbeforeunload

2020-08-10 09:31发布

问题:

I want to send a $http.get if the page gets closed. Then I stumbold upon a problem where
promises can't get resolved because if this one last method returns, the page gets destroyed.

The following does not work because onbeforeunload can't resolve promises / does not wait for them:

window.onbeforeunload = function(
    $http.get('http://someth.ing/update-state?page=unloaded').then(function(){
        // never called... never sent...
    }

}

I know, you can use default sync HTTP Methods but the question is generally how could we sync/wait for promises to resolve here.

My idea would be something like this:

window.onbeforeunload = function(){
    var ready = false;

    $http.get('http://someth.ing/update-state?page=unloaded').then(function(){
         ready=true;
    }

    // I don't see any other opportunity to sync a promise here than this:
    while(!ready) angular.noop();
    // big question: did $http call the server now? Can we finally return in this method to let the browser 'exit' the page?
}

RFC

回答1:

You have 2 problems:

  • You are using a callback (event handler) intended as "last-chance", this event handler, if browsers typically treat it in a special way, and doing any kind of long-running operations here is not intended.
  • Standard operation workflow will resume after this callback exits, meaning that any asynchronous operations are likely not going to be finished, especially if they are guaranteed to not be executed in the current tick (promises fall under this category, they will never be resolved synchronously by design).

So what to do to work within these limitations? You have a few options:

  • Avoid asynchronous code. You can do the http fetch before you get to this point, store the results and use them synchronously inside onbeforeunload.

  • Use ServiceWorker to continue doing work after page has been terminated. Again, I suggest creating, registring and activating worker before you get to handling onbeforeupload. During onbeforeupload handling - you want to postMessage to your worker, asking it to do some work for you. This option comes with 2 caveats:

  • ServiceWorker is not a released standard, it's still a draft, expect API changes, browser support issues and, indeed, potential scrapping of the API all together.

  • Worker has no direct access to your page, you have to be aware of that, what what it means. For example - no window object, no libraries loaded on your page, separate script for the worker, exchange of data via explicit messages only - to name a few.