4

In this coding example the function logout() won't execute all it's async calls and won't wait till they are finished – instead page is unloading before, because the return of the beforeunload event triggers unloading the page.

$(window).on('beforeunload', function(event) {
    event.preventDefault();
    logout();
    return;
});

What I want to try is that the event function returns AFTER several asynchronous calls in logout() are finished.

EDIT: My goal is NOT to show an alert with this! I only want to execute some code before the page is unloaded. The logout function could contain ajax requests and jQuery animations with some duration that needs to be finished.

I tried with callbacks, but end up with this, what isn't the desired result since it's returning to the callback, not to the event function.

$(window).on('beforeunload', function(event) {
    event.preventDefault();
    logout(function(x) { return; });
});
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
ScientiaEtVeritas
  • 5,158
  • 4
  • 41
  • 59
  • Possible duplicate of [jquery beforeunload when closing (not leaving) the page?](http://stackoverflow.com/questions/18783535/jquery-beforeunload-when-closing-not-leaving-the-page) – E. Mourits Jun 27 '16 at 21:07
  • 1
    @E.Mourits No, have you read my question? It has nothing to do with the distinction between leaving and navigating, but to execute code before the page is unloaded / the new page is loaded. – ScientiaEtVeritas Jun 27 '16 at 21:15
  • As far as I know you cannot stop a user from leaving your page. The possible duplicate is an answer I thought is closest to what your want. – E. Mourits Jun 27 '16 at 21:19
  • @E.Mourits: I can. The beforeunload function is executed before. If there is a while(true) in it, the user can't leave. I don't see that your reference has something to do with my question. – ScientiaEtVeritas Jun 27 '16 at 21:24
  • Ok..you can, but you really shouldn't. Why do you want to the user out when he/she tries to leave? Adding a logout button to your page works just as well. – E. Mourits Jun 27 '16 at 21:31
  • @E.Mourits: The logout is just an example. I don't want to stop the user to leave, but want to show something for 500 ms before he's leaving. It's about a Google Chrome Extension – I inject some code into the page and it shall show something before the page is unloaded. – ScientiaEtVeritas Jun 27 '16 at 21:36
  • You may want to add the [tag:google-chrome-extension] tag, as it could make a difference in the answers (I don't know for sure, but extensions have hooks to the browser that web pages don't). – Heretic Monkey Jun 27 '16 at 21:53
  • the only thing that actually solves this problem is to make the request synchronous. This is one of those few cases where `async: false` is appropriate. the animations on the other hand aren't possible. – Kevin B Jun 27 '16 at 21:57
  • the better alternative is usually to stop the event that causes them to leave the page before you get to this point, however i'm not sure how that would actually work for a browser extension. – Kevin B Jun 27 '16 at 21:59
  • @KevinB For ajax calls I know this property, but you can't do this for jQuery animations and other async function calls, so this is not a general solution. – ScientiaEtVeritas Jun 27 '16 at 22:00
  • @ScientiaEtVeritas you can't do anything asynchronous within this particular event callback. it simply isn't possible, by design. – Kevin B Jun 27 '16 at 22:01
  • @KevinB I already tried to catch click events on a-tags. But this solution has many false positives even if you check the link on anchors (#) etc. So I landed here with this event, what actually is exactly the event I'm looking for, but with the problem described above. – ScientiaEtVeritas Jun 27 '16 at 22:05
  • that and keeping middle click working, etc, yeah catching click events is a poor solution too. There really isn't a good solution. You could try something similar to what google does with search results; on click, if the href targets a different domain/protocol change the href to a proxy page, do the work there, then redirect. You still won't get animations though. – Kevin B Jun 27 '16 at 22:06

3 Answers3

3

Since everything executed on the page would become invalid when the page is unloaded, you can't depend on the page itself to complete the async call.

One wordaround for chrome extension would be making use of background page. You could simply send message to background page inside beforeunload handler, catching all info you need to deal with, and execute the async call in background page. Sample code would be:

content.js

window.addEventListener('beforeunload', function() {
    chrome.runtime.sendMessage({ info: "Here is the info you would like to pass to background page"});
});

background.js

chrome.runtime.onMessage.addListener(function(request) {
    // The following is your async call
    logout();
    // Don't forget the following code
    return true;
});

Don't forget to return true from the event listener in background page, since chrome.runtime.onMessage.addListener would become invalid when the event listener returns, see this answer for more details.

Community
  • 1
  • 1
Haibara Ai
  • 10,703
  • 2
  • 31
  • 47
  • This is a good idea for backend tasks. But can I show something from the background script, maybe with chrome.tabs.executeScript – but I think it would be refreshed as well and would not stay as long I want it to stay? – ScientiaEtVeritas Jul 15 '16 at 07:19
2

Try using async/await for your handler, that is:

$(window).on('beforeunload', async function(event) {
    await logout();
    event.preventDefault();
    event.returnValue = false; // Seems require this for Chrome
});

Of course, you should have Promise returned from logout();

But I'm not sure if this is reliable.

hankchiutw
  • 1,546
  • 1
  • 12
  • 15
  • 1
    marking the event handler as async actually works reliably in Chrome. Very nice. – Matt Wonlaw Mar 03 '21 at 21:52
  • 4
    This doesn't works. The async function returns a Promise and if the calling ($(window).on...) doesn't wait, it continues before the promise is resolved and the page is unloaded before the await logout() completes. async/await doesn't make asynchronous calls synchronous. It is just a syntactic sugar for Promises. – Emanuele Benedetti Mar 10 '21 at 13:31
  • 1
    This solution does not wait that the call is completely executed before unload. If you replace `await logout()` with `await new Promise(resolve => setTimeout(resolve, 12000));` page still just unloads immediately. – Mikael Lepistö Jan 24 '23 at 11:08
-7

Not really a clean solution but you can try setTimeout to force the code to wait while the logout is in progress.

var timeToLogoutInMs = 500;
setTimeout(function() {
    // Nothing
}, timeToLogoutInMs);

EDIT: Why do you need to do this on the beforeunload hook? Why not set a manual option user to log out?

tetutato
  • 638
  • 5
  • 16
  • As far as I know setTimeout is executed asynchronously which means that it would immediately continue with the next statement (the return). The logout is just an example – actually it's about a Google Chrome Extension. I inject some code into the page and want to show something before the page is unloaded. – ScientiaEtVeritas Jun 27 '16 at 21:30
  • This would not change the behavior. – Kevin B Jun 27 '16 at 21:56
  • Hmm I see. What happens if you call alert in the same hook? Would the alert pop up pause the unload? – tetutato Jun 27 '16 at 22:07
  • @tetutato You're right that an alert would be synchronous, but you are not allowed to put an alert in there: „[...] window.showModalDialog(), window.alert(), window.confirm(), and window.prompt() methods may be ignored during this event.“ – but a while(true) for example would stop it from unloading. – ScientiaEtVeritas Jun 27 '16 at 22:11