onbeforeunload is executed once only - javascript

I use onbeforeunload to reset a variable with Ajax request. But it is executed once only. E.g if I visit the page (after log in) and close the window, the function is executed, but if I repeat the operation (putting the address in the browser with login done and closing the window) the function isn't executed.
window.onbeforeunload = function (e) {
//Ajax request to reset variable in database
resetDemoValueWithAjax();
};
//Ajax request to reset variable in database and exit
function resetDemoValueWithAjax(){
$.ajax({
async:true,
type: "POST",
dataType: "json",
url:"http://localhost/site/index.php/welcome/resetDemoValue",
data: {name: "demo"},
success:function(){
document.location.href='http://localhost/site/index.php/auth/logout';
},
error: function(){
document.location.href='http://localhost/site/index.php/auth/logout';
}
});
}
document.location.href='http://localhost/site/index.php/auth/logout'; Only is used in another moment: When the user does logout. Not here

You have a race condition. The race is between the asynchronous Ajax request and the browser's attempt to navigate away from the page (which is what caused beforeunload in the first place). The navigation away will probably always win, so the browser moves away from the page before the Ajax callback can complete.
If you made the Ajax request synchronous with async:false, this race wouldn't happen, since the JavaScript thread would block until the Ajax request resolved. Be aware that this makes for a potentially annoying user experience (i.e., the user tries to close the page but has to wait for the Ajax request to resolve before the tab will close).

Related

How to cancel MVC action if I use AJAX to call it?

I have an AJAX function on a view that calls an Action on one of my controllers to get a JSON object. The problem is that it takes a while to create the object. If the user reloads the page, then the AJAX call runs again. As far as I can tell the action is still running from the last request, but the response is no longer relevant because the new AJAX call is waiting for the new response.
My question is, how do I keep the action from continuing if the user leaves the view that called it?
Ajax:
$.ajax({
type: "get",
url: "/Alert/GetWallboardAlerts",
cache: false,
dataType: "json",
contentType: "application/json; charset=utf-8",
success: function (result) { ... }
});
Controller action:
public ActionResult GetWallboardAlerts()
{
//...do long running stuff
//...still doing stuff when page reloaded or I navigate elsewhere
return Json(alertString, JsonRequestBehavior.AllowGet);
}
I don't think you can. Once you made a request to the server, you could close the browser but the request will still be executed.
This is just an idea:
1) Caller makes an AJAX request and provides a unique value (could be a guid).
2) Handle the page reload event and the close event and send a new AJAX request with the same unique key requesting a cancellation.
3) The long process task that is executed on the server should check for a flag that indicates that the task has been canceled.
You could also use a WebSocket for a task like this so the server could ping the client (like a "still alive" request). If you are using .NET you could use SignalR.
I hope it helps or at least it gives an idea to solve your problem.

Javascript not proceeding aynchronously beyond AJAX call

When the user presses the 'Process' button on my application, I would like the application to trigger an AJAX request and then immediately redirect the user to another screen without waiting for the results of the AJAX request. I believe I have coded it appropriately but I notice that the screen is waiting for the AJAX to finish before redirecting. Am I missing something below?
$('#process-btn').on('click', function()
{
// disable the process & cancel buttons to prevent
// double submission or interruption
$('#cancel-btn').addClass('disabled');
$(this).addClass('disabled');
// trigger the AJAX require to process the uploaded file on the server side
$.ajax({
url: $('#form').attr('action'),
type: 'post',
data: $('#form').serialize(),
success: function() {
//on success
}
});
// redirect the user to view list
// this line is not being called immediately -
// this is being called only after AJAX returns
window.location.replace( www_root + 'Home/index' );
});
Because the button you have this handler hooked to is a submit button for a form (per your comments) and you aren't preventing the default behavior of that button, then the form submit will happen immediately and when the submit returns, it will change the page regardless of what your code tries to do.
So, the issue is that the returned form submit was overcoming what your code was trying to do.
You may be living a little dangerously by redirecting before your ajax call has finished. It's possible the browser could drop the ajax connection before the TCP buffers had actually been sent as TCP often has a small delay before sending buffers in order to collect consecutive data into common packets. It would be much safer to either redirect after a short timeout or redirect on the complete event which will be called regardless of ajax success.
If you really want to do the redirect BEFORE the ajax call has completed, you can experiment with the timeout value (shown here as set to 500ms) in this code to see what works reliably in multiple browsers:
$('#process-btn').on('click', function(e) {
// prevent default form post
e.preventDefault();
// disable the process & cancel buttons to prevent
// double submission or interruption
$('#cancel-btn').addClass('disabled');
$(this).addClass('disabled');
// trigger the AJAX require to process the uploaded file on the server side
$.post($('#form').attr('action'), $('#form').serialize());
// redirect the user to view list
// this being called after a short delay to "try"
// to get the form ajax call sent, but not "wait" for the server response
setTimeout(function() {
window.location.replace( www_root + 'Home/index' );
}, 500);
});
Also, note that I've added an e.preventDefault() and added the e argument to the event handler to make sure the form is not posted by default, only by your ajax code.
And, the timeout time is set here to 500ms. What you need is enough time for the TCP infrastructure in the host computer to send all your form data before you start the redirect. I see a mention of a "file upload" in your comments. If this form is actually uploading a file, that could take way, way longer than 500ms. If it's just sending a few form fields, that should go pretty quickly assuming there are no connection hiccups.
Caveat: Doing it this way is not the 100% reliable way of getting data to your server. There can easily be some conditions where it takes longer than usual just to do a DNS lookup before connecting with your server or your server could momentarily take longer to respond to the initial connection before data can be sent to it. The only 100% reliable way is to wait until the ajax call has succeeded as mentioned elsewhere.
You could perhaps have the best of both worlds (reliability + fast response) if you changed the way your server processes the ajax call so that as soon as it has received the data, it returns a successful response (e.g. in milliseconds after receiving the data) and then after it has sent back the successful response so the browser can then reliably do its redirect, it takes it's 2-3 minutes to actually process the data. Remember, you don't gave to wait until you are done processing the request to return a response. Then, you know that the server has received the data, but the browser doesn't have to wait for the processing time. If you don't always want this ajax call to work that way, you can pass an argument to the ajax call to instruct the server whether you want the fast response or not.
Why not try this:
$.ajax({
url: $('#form').attr('action'),
type: 'post',
data: $('#form').serialize(),
success: function() {window.location.replace( www_root + 'Home/index' );}
});

Efficient way of passing data and calling background script in PHP

I have a page where I show 5 questions to a user and when he clicks on Next 5 link I am sending the score of current page onbeforeunload() to the script updateScore() asynchronously using jQuery AJAX and when the call is successful the next 5 questions are displayed.
window.onbeforeunload=function()
{
$.ajax({
type: "POST",
url: "updateScore.php",
data: "pageScore="+score,
cache: false,
timeout:5000,
async:false
});
}
But the problem is that on slow connections,it might hang the browser for a while until AJAX call returns successfully.When I tried async:true(default) the next page is loaded first without making call to updateScore.php.It might be due to the fact that connection is fast in localhost hence giving no time for the AJAX call to complete.This was the reason I used async:false.Will it happen (making no AJAX call) if I use async:true in practical case as well?If yes, is there a way to come around this problem?
I advice you to change your code a bit.
Make ajax request on "click" event, and redirect user inside ajax callback function.
Like this:
$('#mybutton').on('click', function()
{
$('#pleasewait').show();
$ajax({
type: "POST",
url: "updateScore.php",
data: "pageScore="+score,
success: function() { document.location="nextpage.php" }
});
}

Page waits for AJAX before changing location

This question might seem a bit odd, the problem arised when the page went through webtests.
The page uses an AJAX call (async set to true) to gather some data. For some reason it won't swap pages before the AJAX call has returned - consider the following code:
console.log("firing ajax call");
$.ajax({
type: "POST",
url: "requestedService",
data: mode : "requestedMethod",
cache: false,
dataType: "json",
success: function() { console.log("ajax response received") },
error: null,
complete: null,
});
console.log("changing window location");
window.location = "http://www.google.com"
The location only changes after AJAX returns the response. I have tested the call, it is in fact asynchronous, the page isn't blocked. It should just load the new page even if the AJAX call hasn't completed, but doesn't. I can see the page is trying to load, but it only happens once I get the response. Any ideas?
The console output is:
firing ajax call
changing window location
ajax response received
This seems to work fine for me. The location is changed before the code in the async handler executes. Maybe you should post some real code and not a simplified version, so that we can help better.
Here is a demonstration that works as you expect: http://jsfiddle.net/BSg9P/
$(document).ready(function() {
var result;
$("#btn").on('click', function(sender, args) {
setInterval(function() {
result = "some result";
console.log("Just returned a result");
}, 5000);
window.location = "http://www.google.com";
});
});
And here is a screenshot of the result: http://screencast.com/t/VbxMCxxyIbB
I have clicked the button 2 times, and you can see in the JS console that the message about the location change is printed before the result each time. (The error is related to CORS, if it was the same domain, it would navigate).
Bit late but maybe someone else will have the same issue.
This answer by #todd-menier might help: https://stackoverflow.com/questions/941889#answer-970843
So the issue might be server-side. For eg, if you're using PHP sessions by default the user's session will be locked while the server is processing the ajax request, so the next request to the new page won't be able to be processed by the server until the ajax has completed and released the lock. You can release the lock early if your ajax processing code doesn't need it so the next page load can happen simultaneously.

Could it be, that Chrome cancels pending Ajax requests, once the browser is being redirected to another URL?

I have this function to unlock a list the user is currently editing:
function unsetLock(id) {
$.ajax({
type: "POST",
url: "/ajax.php?action=unsetLock",
dataType: 'json',
data: "id="+ id
});
return true;
}
When the user navigates away from the list, I have to cancel the lock:
unsetLock(lockID);
document.location.href='/page/to/navigate/back/to.php';
However this unlock sometimes works and sometimes doesn't. I think it is because document.location.href is executed, before the ajax call has actually been sent to the server.
How can I force to send the unlock before navigating the user to the next page?
Actually I don't need to wait for the Ajax-Reply, since I want to redirect the user, whether it succeeds, or not. I just want to make sure, it is being transferred to the server.
If I place the document.location.href inside the Ajax function, it will wait for the reply.
A really bad-mannered way to do it is to add: async: false, which will lock the browser up until the AJAX call is complete.
Of course, if there is a problem and the AJAX call never completes...
It's the quickest and easiest solution to your problem, but probably not the best.
I, personally, would have the lock only last for twenty seconds (using a timestamp in the database), and send an ajax call every ten seconds to re-lock the page (if that makes sense) using setInterval().
That way the lock will unset itself a few seconds after someone leaves the page, and is good no matter what the situation (a power failure for the client wouldn't leave the page locked forever, for example).
Perhaps I'm missing something, but why not use the success option in the Ajax call? This will execute whatever the outcome and makes sure it reaches the server.
function unsetLock(id) {
$.ajax({
type: "POST",
url: "/ajax.php?action=unsetLock",
dataType: 'json',
data: "id="+ id,
success: function(){
document.location.href='/page/to/navigate/back/to.php';
}
});
return true;
}

Categories