2

So this is really bugging me, I don't know if it is a browser related glitch or javascript just works that way ( I hope it does). I created a fiddle. https://jsbin.com/laluziqede/1/edit?html,js,output

Open your console, then click the button. When the dialog appears the function continues normally (first console.log isn't paused), however the one inside setTimeout function is paused and will only show after you click 'stay on page'.

But why, could someone explain this? I want to use this property in my application (execute an action right after user clicks stay), but I'm not sure if it's a good practice and is it working on all browsers and devices.

Edit: Here's the code from the bin:

$(window).on('beforeunload', function() {

   return 'Check your console please and then click stay';

});


$('#click-me').on('click', function() {

    window.location.href='about:blank';

    console.log ('dialog won\'t stop me from showing');

    var timer=setTimeout(function() { 
        console.log('this was paused by the dialog');
    },0);

});
Maciej Krawczyk
  • 14,825
  • 5
  • 55
  • 67
  • This is only a guess so I'll post it as a comment, but I'm guessing it has to do with the fact that JavaScript is single threaded. The `timer` function, though marked at `0` seconds is a callback function so it falls after the `beforeunload` event in the execution chain. – War10ck Aug 31 '15 at 17:00
  • Can you please post the code that you are using here on SO, instead of in some bin? – Bergi Aug 31 '15 at 19:04

2 Answers2

2

Javascript is single threaded (unless you start using things like WebWorkers and other newer technologies). So the timer function schedules something to be done, but it will only be done when everything else has yielded control of the javascript thread. So timer is only asynchronous in the sense that you're asking for some work to be done after some period of time, but it is not truely asynchronous in the sense that that something can be done while something else is also being done.

This applies to things like XHR requests as well, even though the XHR request are indeed dispatched asynchronously, the responses are all handled synchronously one at a time.

Your specific example is a bit odd in that it's not another javascript function that is blocking, it's a browser security feature that is making sure you want to let the previous javascript operation take you away from the current page. The concept is the same though.

CodingGorilla
  • 19,612
  • 4
  • 45
  • 65
2

The behaviour is browser dependent. I tested it in Firefox, Chrome, IE and Edge, and of those only Chrome has the behaviour that you describe.

The difference lies either in when the beforeunload event is triggered, or when it is handled. Most browsers trigger the event immediately when you change the location property and also handle it immediately. Chrome either triggers and handles the event when the navigation is actually about to happen, or places the event on the queue and handles it later just like regular events.

In Chrome the code inside the setTimeout handler will not happen until after the beforeunload event is handled, either because the navigation is handled before any queued events, or because the timout event is after the unload event in the queue.

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • Are you sure that the event handler is executed synchronously (from within the assignment)? If yes it should block execution even if the latter statement would not be in that timeout. I do think that the `setTimeout` is executed immediately, but the callback is put in the event queue which is *blocked* while the modal popup is shown… – Bergi Aug 31 '15 at 18:49
  • @Bergi: If the event wasn't handled synchronously, then the code that isn't in the timeout would run before the dialog, and it doesn't. – Guffa Aug 31 '15 at 19:02
  • If I understood the OP correctly, the code that isn't in the timeout (the "*dialog won't stop me from showing*" and the `setTimeout()` call itself) *does* run before the dialog. Only the code in the timeout callback is blocked by the modal. – Bergi Aug 31 '15 at 19:06
  • @Bergi: Yes, it does, but only in some browsers. Out of Firefox, Chrome, IE and Edge only Chrome does that. – Guffa Aug 31 '15 at 19:08
  • Doesn't the code outside of the timeout *always* run, in every browser? I thought it was only the code in the callback that was sometimes blocked and sometimes not. – Bergi Aug 31 '15 at 19:11
  • @Bergi: No, the code outside the timeout is blocked, except in Chrome. – Guffa Aug 31 '15 at 19:22
  • Thanks for your answer. I tested it in firefox and both console outputs show after clicking 'stay'. So now I know that normal execution being paused is browser dependent, but what about the setTimeout callback? **Will code inside setTimeout callback be always blocked or is it also browser dependent**? Basically I need to do an action right after user clicks either of the buttons (stay or leave) and I'm wondering if setTimeout is a reliable way to do that. I can't use on load event or at least it's not that simple in my case, and simplest solutions are the best solutions. – Maciej Krawczyk Aug 31 '15 at 20:39
  • @Iron96: Well, it would be a reliable way to keep the code from running before the dialog, but it's not certain that it will run at all. The browser is in the process of making the current page go away, so it will stop handling events at any moment. – Guffa Aug 31 '15 at 20:51
  • I forgot to mention it's about iframes. Iframe location is subject to change and the script executes in parent page So it will run always. Thanks again. – Maciej Krawczyk Aug 31 '15 at 21:18
  • @Iron96: Then you should make sure that the timeout is bound in the parent page, otherwise it will go away when the iframe page goes away even it the callback is in the parent page. – Guffa Aug 31 '15 at 21:22