Why does 'stay on page' dialog pauses asynchronous function? - javascript

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);
});

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.

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.

Related

Using setTimeout in javascript to simulate php's sleep

I have an AJAX function which takes some time to complete. When it's done I want to open a new tab with the result. To do so I've came up with three options:
Use window.open() from the AJAX-call
Use window.open() in the javascript function/event trigger.
Use window.open() in a setTimeout-function within the javascript
function/event trigger.
The problem is this has to work with all major browsers (IE, Chrome, Firefox and Safari) and option 3 should do the trick but is has unwanted side effects:
In Chrome the window is not opened in a new tab but as a pop up.
In Safari the internal popup prevention is activated; resulting in
not opening the popup. (source)
Now I figured to use setTimeout() as a piece of procedural code and ending up with some like:
$('.selector').click(function() {
doAjaxCall();
setTimeout(function(){ }, 150);
window.open(...);
});
Well, this works for Safari but Chrome and FireFox seem to ignore the setTimeout() and continue directly to the window.open(). Herein lies my problem; the data that has to be used isn't always up to date when window.open() gets called.
So, here I am. Back to basics. Figured out what the symptoms are, knowing the downsides of my explored scenario's and ended up with a something like this:
$('.selector').click(function() {
doAjaxCall();
for(i = 0; i <= 100000000; i++) {
// procedural and time consuming so doAjaxCall has enough time to complete
}
window.open(...);
});
In my case I'm stuck to xajax for the ajax-handling, so I can't use jQuery's ajax solution.
Any suggestions on how to improve this? Resulting in a solution where all major browsers open the popup when the ajax-function has completed?
First of all, the browsers don't ignore the setTimeout function, but the execution doesn't stop and wait for a setTimeout completion, but will continue it's execution and wait for the timeout to complete in order to execute the method passed as argument to setTimeout method.
But you don't need to simulate the sleep method from php in order to achieve what you want.
The ajax call has a success event which you can attach an event handler on. Open your new tab when the success event occurs. Otherwise, you wont be able to know exactly when the ajax request ended. If you can't use the jQUery's ajax, you can create an 'old-school' ajax request, like here
Also, opening a new window is not the best solution, because most browsers have popup blockers.

HTML page unload is not getting called always in IE

I have an onunload method written on the body of a form. Since the page got large amount contents to be streamed from the server,it takes a while to download the entire form. While the form gets downloaded to the browser,if the user clicks the window close button, the unload event (written to call a server side action to reset some flag) is not getting triggered sometimes. The unload method is written towards the end of the page, is that a reason for this? Is there anyway by which we can make sure that onunload is guaranteed to be called?
The thing that hits you is probably the fact that IE doesn't fire an unload event if window.onload hasn't fired yet. The solution is described in this blog post: http://blog.moxiecode.com/2008/04/08/unload-event-never-fires-in-ie/
There are two reasons. One, like you said, the browser may not even be reaching the unload, so you should declare it above your body tag.
The other is that, as GoldenNewby already stated, you need to give the code some time to finish, such as an alert. This is because the JavaScript is cleared from memory the moment the next page is ready, and execution is simply stopped on the old script. If you can guarantee the script will take less time than a page load, you won't need an alert, but if it could take more than 2ms or so to execute, you will need to buy some time.
IE has an onbeforeunload event, you can use that instead. Just set a flag so that your onunload can exit early if onbeforeunload already did its thing.
window.onunload = window.onbeforeunload = function () {
if (window.isAlreadyUnloaded) return;
window.isAlreadyUnloaded = true;
// do your stuff here.
}

Chrome/Safari not calling unload in some cases

Have been stuck with this issue for a few days now, and really need, and would appreciate some help. My requirement is that I want to make a server side callback to clear off some objects when the user navigates away from our page, without clicking logout. For business reasons, our ASP.NET session timeout has to be set to a very high value. Further, I do not want to popup a alert/dialog to force the user to return to the page and click Logoff.
The solution I have arrived at thus far is to make a AJAX callback by embedding this javascript in the page.
window.onunload = pageleave;
function pageleave() {
alert('test');
PageMethods.CheckLogout('abc','xyz',OnSucceed,OnFail);
}
Here is the problem though :
For IE and Firefox, the unload fires, the alert is seen, and I see the callback on my C# side in all the cases I desire
a) User closes browser
b) User types in a new URL in the address bar
c) User clicks on a link causing page to reload
For Chrome and Safari, cases a and b work fine. However, when the user clicks on a link, which causes my aspx page to reload, my C# side code is not invoked. The javasacript alert is fired though.
I am trying to see how I can get Chrome/Safari to behave like IE/Firefox. Is this even a possibility?
Thanks in advance for the help,
Rajesh.
Use the beforeunload event instead of the unload event.
Also, use a synchronous AJAX request, not an asynchronous one, so that the request is completed before your beforeunload function exits.
I'm not sure how you would do a synchronous AJAX request using your JavaScript framework. In jQuery, it would look like this:
window.onbeforeunload = function() {
jQuery.ajax({
url: '/page/to/load',
async: false
});
};
In case anyone comes across this in the future, I couldn't use window.onbeforeunload as it was firing too early. Instead, I found window.onpagehide as a suitable workaround.
e.g.
window.onpagehide = function() {
// some code here.
};

jquery: unload or beforeunload?

I want to post an message to the server when user navigate off from the current page, I am using .unload right now but the result is unreliable, even in its document is said true:
The exact handling of the unload event
has varied from version to version of
browsers. For example, some versions
of Firefox trigger the event when a
link is followed, but not when the
window is closed. In practical usage,
behavior should be tested on all
supported browsers, and contrasted
with the proprietary beforeunload
event.
Should I use beforeunload event? Is it reliable?
Yes, beforeunload is more reliable, but be sure to assign it directly (not bound through jQuery), like this:
window.onbeforeunload = function() { /* do stuff */ };
The unload event itself wasn't meant for work to be done, only cleanup of objects...as garbage collectors get better and better, there's less reason for the browser to even fire the unload event.
Also be aware that for your specific case you'd have to make a synchronous request to the server...otherwise the browser still won't wait for the AJAX call to complete.

What event is fired when window.print() is called?

I am trying to work through this question and I have had little success tonight. I think I can make the code below work if I only knew what event was fired when the window.print() function is called.
var browser_name = navigator.appName;
if(browser_name == 'Microsoft Internet Explorer'){
window.attachEvent("print()",on_print_function);//I realize I cannot attach an event listener to a function, I just wanted you to see what I am trying to accomplish
}
else{
window.addEventListener("print()",on_print_function,false);
}
The function that is called when the print event takes place returns a page that stores some info in the database.
My end objective is to have the page print ONLY if the info in question is going to be stored in the database. I am open to better ways of tackling this, but I think I will have it going ok if I can just get the event for the print() as I said.
EDIT
I am giving up on this for now, I have settled with another way of doing what I want. I look forward to the day when FireFox supports onbeforeprint() and onafterprint().
Well as far as I know, IE has several events line onbeforeprint() and onafterprint() but they are not supported by other browsers. So relying on this is not very good.
Perhaps you can have a print button on your page. Attach to it a handler which executes the ajax call to the server to store the data to the database and on success of this call, call the print() on the window.
Is that what you are looking for ?
Apparently you can now listen for this with Firefox (but not other browsers):
Some browsers (including Firefox 6 and later and Internet Explorer) send beforeprint and afterprint events to let content determine when printing may have occurred. You can use this to adjust the user interface presented during printing (such as by displaying or hiding user interface elements during the print process).
The afterprint event is raised after the user prints or aborts a print dialog.
https://developer.mozilla.org/en-US/docs/Web/API/window.onafterprint
You may be able to emulate this with media queries in other browsers: https://stackoverflow.com/a/18325463/421243

Categories