This question already has an answer here:
Asynchronous ajax request locking browser
(1 answer)
Closed 4 years ago.
I have page that has two main queries - an item and similar items.
The item is fetched as normal but the similar items are loaded as a separate jQuery ajax call which automatically triggers as the page loads. The similar items are then appended to the page. The second query is separated because it can occasionally be slow and affect user experience.
$.post(url, function(output) {
if (output) {
var data = $.parseJSON(output);
if (data.success) {
$("div").html(data.html);
}
}
});
I'm now noticing that the ajax query holds up the page even when trying to navigate away. The browser won't allow a user to leave the page until the ajax query returns something - either similar items or no results.
I haven't dealt with this scenario before. Is there a way to structure this JQuery ajax call in a way that wouldn't force the user to wait for it to complete? Addressing the speed of the server-side response is not an option at the moment.
UPDATE/ANSWER:
Posted below as an answer.
The behavior you describe looks to be "the browser is locked while the request is active".
You possibly have something like the code below somewhere:
$.ajaxSetup({
async: false
// And other parameters...
})
From the documentation:
Note that synchronous requests may temporarily lock the browser, disabling any actions while the request is active.
So... The only way to revert that is to find that code chunk and change it... OR to just redefine the async parameter again, just before the $.post() executes:
$.ajaxSetup({
async: true
})
I eventually found the answer here:
Asynchronous ajax request locking browser
PHP on the server is locking the session and it can be unlocked using php session_write_close() in the server-side code called by the ajax. Although nothing has been returned by the ajax call, clicking on a link needs the session for the next page, which is still hanging from processing the ajax call.
Related
This question already has answers here:
How to perform an ajax call on page unload?
(3 answers)
Closed 10 months ago.
I'm trying to send a PUT request on the unload event, however the request is always cancelled.
Answers here suggests it's
not possible: How to perform an ajax call on page unload?
possible: How do I send an AJAX request upon page unload / leaving?
Can I send an AJAX request on unload, if so how can I without being cancelled.
Note: I don't care about the response, I don't need a response. Just need to send some info to the server saying the page was closed without keep alive checks.
The question is a duplicate (I should have realized that at the time). I've posted a version of this answer here on the other question and made this a Community Wiki.
Doing standard ajax calls in a page unload handler is being actively disabled by browsers, because waiting for it to complete delays the next thing happening in the window (for instance, loading a new page). Although you can't do PUT with it, the replacement for synchronous ajax in unload handlers is sendBeacon. sendBeacon lets you send data to your server without holding up the page you're doing it in:
window.addEventListener("unload", function() {
navigator.sendBeacon("/log", yourDataHere);
});
The browser will send the data without preventing / delaying whatever is happening in the window (closing it, moving to a new paeg, etc.).
Beacons are POSTs, not PUTs, but other than that it's exactly what you're looking for.
Note that the unload event may not be reliable, particularly on mobile devices. You might combine the above with sending a beacon on visibilitychange as well.
All you need to do is make the ajax call synchronous. That way the browser will wait for the response before closing the window/tab and it will not be cancelled.
$(window).unload(function(){
$.ajax({
type: 'PUT',
url: '/your_url.php',
async:false, //IMPORTANT, the call will be synchronous
data: {}
}).done(function(data) {
console.log('complete');
});
});
My page loads all necessary data from the server at startup via AJAX. This includes user's language settings, various classifiers, some business data etc.
The problem I am facing is that when the user first comes to the page, all these different AJAX calls are kicked off at the same time. This means that on the server side, most of them are assigned different JSESSIONID-s (I am using Spring on Tomcat 8 without any complex configuration). As a result, some of the data is initialized on the server side in one session, but the browser might end up using a different session in the end and does not have access to the data set up by earlier ajax calls.
I wanted to solve this by using a fast synchronous AJAX call in the very beginning so that after it returns and gets a JSESSIONID, all subsequent calls would be made in this original session.
$.ajax("api/language", {
type: "GET",
cache: false,
async: false,
success: function(data) {
//do stuff;
}
});
// more AJAX calls
It works, but I get warning messages that synchronized XMLHttpRequest on main thread is deprecated. Now - I understand the reasons why such a synchronized call is bad for UI in general, but what other options are there available for me if I want to force all AJAX calls to use the same server side session?
I can achieve the same result by using a callback and just placing all the rest of my page initialization code in there, executing it in the 'success' section of the first AJAX call, but that wouldn't that have exactly the same effect as synchronizing on main?
I'd initiate the session when loading the HTML document rather than when requesting something from the API.
Alternatively, trigger the subsequent API calls from the success callback of the first one.
"Hacky" solution
You really give your own solution at the end: wrap everything in an asynchronous AJAX call. It is similiar to the synchronous solution, but this way you can set up a loading animation, or something similar.
"Nice" solution
Another, possible nicer solution. When the user arrives, you can redirect to the starting page of your web application with the generated jsessionid. This can be done with a servlet. I am quite sure that Tomcat can be configured to do this without writing your own code.
I need to execute an action of a controller when a user leave a page (close, refresh, go to link, etc.). The action code is like:
public ActionResult WindowUnload(int token)
{
MyObjects[token].Dispose();
return Content("Disposed");
}
On window download I do Ajax request to the action:
$(window).unload(function ()
{
$.ajax({
type: "POST",
url: "#Url.Action("WindowUnload")",
data: {token: "#ViewData["Token"]"},
cache: false,
async: true
});
//alert("Disposing.");
})
The above ajax request does not come to my controller, i.e., the action is not executed.
To make the code above to work I have to uncomment the alert line, but I don't want to fire alert on a user.
If I change async option to false (alert is commented), then it sometimes works. For example, if I refresh the page several times too fast then the action will not be executed for every unload.
Any suggestions how to execute the action on every unload without alert?
Note, I don't need to return anything from action to the page.
Updated: answers summary
It is not possible reliably to do request on unload, since it is not proper or expected behavior on unload. So it is better to redesign the application and avoid doing HTTP request on window unload.
If it is not avoidable, then there are common solutions (described in the question):
Call ajax synchronously, i.e., async: false.
Pros: works in most cases.
Pros: silent
Cons: does not work in some cases, e.g, when a user refreshes the windows several times too fast (observed in Firefox)
Use alert on success or after ajax call
Pros: seems to work in all cases.
Cons: is not silent and fires pop up alert.
According to unload documentation, with async: false it should work as expected. However, this will always be a bit shaky - for example, user can leave your page by killing/crashing the browser and you will not receive any callback. Also, browser implementations vary. I fear you won't get any failproof even.
HTTP is stateless and you can never get a reliable way to detect that the user has left your page.
Suggested events:
Session timeout (if you are using sessions)
The application is going down
A timer (need to be combined with the previous suggestion)
Remove the previous token when a new page is visited.
Why does this need to happen at all?
From the code snippet you posted you are attempting to use this to dispose of objects server side? You are supposed to call Dispose to free up any un-managed resources your objects are using (such as Database connections).
This should be done during the processing of each request. There shouldn't be any un-managed resources awaiting a dispose when the client closes the browser window.
If this is the way you are attempting this in the manner noted above the code needs to be reworked.
Have you tried onbeforeunload()?
$(window).bind('beforeunload', function()
{
alert('unloading!');
}
);
or
window.onbeforeunload = function() {
alert('unloading!');
}
From the comment you made to #Frazzell's answer it sounds like you are trying to manage concurrency. So on the chance that this is the case here are two common method for managing it.
Optimistic concurrency
Optimistic concurrency adds a timestamp to the table. When the client edits the record the timestamp is included in the form. When they post their update the timestamp is also sent and the value is checked to make sure it is the most recent in the table. If it is, the update succeeds. If it is not then someone else got in sooner with an update so it is discarded. How you handle this is then up to you.
Pessimistic concurrency
If you often experience concurrency clashes then pessimistic concurrency may be better. Here when the client edits the record a flag is set on that row to lock it. This will remain until the client completes the edit and no other user can edit that row. This method avoids users loosing changes but add an administration over head to the application. Now you need a way to release unwanted locks. You also have to inform the user through the UI that a row is locked for edit.
In my experience it is best to start with optimistic concurrency. If I have lots of people reporting problems I will try to find out why people are having these conflicts. It maybe that I have to break down some entities in to smaller types as they have become responsible for doing too many jobs.
This wont work and even if you are able to somehow make it work it will give you lots of headaches later on, because this is not how the browser/HTTP is supposed to be used. When the page is unloading (in browser) the browser will call the unload event and then unload the page (you cannot make it wait, not even my making sync ajax calls) and in case the call was going on and the browser after executing the code unload the page, the call will also get cancelled and thats why you see the call on server sometimes and sometimes it doesn't work. If you could tell use why you want to do this we could suggest you a better approach.
You can't. The only thing you can do is prompt the user to stay and hope for the best. There are a whole host of security concerns here.
i have a question regarding partial page loading with AJAX.
Suppose that an user clicks on a button that makes an AJAX call to load part of a page (it can possibly include dynamically loaded JS and/or CSS), and the html content is dropped on some div. Then, before the first load is complete he clicks on another button that makes another AJAX call that drops other content on the same div. How should i prevent this behaviour to create any conflicts? Some possible conflicts might be something like, for example, the first load executes some JS on content that is not found because the second load already changed that div.
Thanks in advance.
Edit:
I would appreciate answers based on asynchronous methods. Thanks.
Genesis and Gaurav are right about disabling user interaction. +1 from me to each of them. How you handle the logic is actually quite simple:
$('#my_submit_button').click(function(){
$.ajax({
url:'/my_file.php',
dataType='json',
beforeSend:function(){
$('#my_submit_button').prop('disabled',true);
},
error: function(jqXHR, status, error){
// handle status for each: "timeout", "error", "abort", and "parsererror"
// Show submit button again:
$('#my_ajax_container').html('Oops we had a hiccup: ' + status);
$('#my_submit_button').prop('disabled',false);
},
success:function(data){
$('#my_ajax_container').html(data);
$('#my_submit_button').prop('disabled',false);
}
});
});
make it synchronous (not recommended)
disable link/button while ajaxing
do not mind about it
but in your case it won't do any conflicts because when html is replaced, scripts too
Just disable the buttons that cause the AJAX calls to start while one has not completed yet.
I'm not sure this would actually be a problem for you because Javascript is single threaded. When the first ajax response comes in and you execute some javascript, that javascript cannot be interupted by the second ajax response as long as it is one continuous thread of execution (no timers or other asynchronous ajax calls as part of it's processing).
Let's run through a scenario:
User clicks button - first ajax call starts.
User clicks button - second ajax call starts.
First ajax call finishes and the completion code execution starts for what to do with the new data.
While code is executing from first ajax call, the second ajax call completes. At this point, the browser puts the second ajax call completion into a queue. It cannot trigger any of your completion code yet because the Javascript engine is still running from the first load.
Now the first load completes it's execution and code and returns from it's completion handler.
The browser now goes to it's queue and finds the next event to process. It finds the completion of the second ajax call and then starts the completion code for that ajax call.
As you can see from this scenario which has overlapping ajax calls and the second completing in the middle of the processing the first, there still is no conflict because the Javascript engine is single threaded.
Now, as the other answers have suggested, you make not want this user experience of launching a new request while one is still processing, but they won't technically conflict with each other. You have several tools you can choose from if you want to prevent overlapping calls:
You can prevent starting the second call while the first call is unfinished. You can do this both in the UI and in the actual code.
When there are multiple calls outstanding, you can decide to drop/ignore the earlier responses and not process them - waiting only for the last response.
When the second call is initiated, you can cancel the first call.
You can let the second just replace the first as in the above scenario.
The first two options require you to keep track of some cross ajax-call state so one ajax call can know whether there are others and act accordingly.
When a user requests to edit an entry in our CMS we 'lock' it so that nobody else can edit it simultaneously, we release the lock when they submit their changes. However we need to handle the case where the user leaves the page through other links... my first attempt is to use jQuery to fire a synchronous $.ajax() call on $(window).unload. This does work, however on the next page the user sees, the entry appears to be locked (assuming they return to the page listing all the entries, perhaps via the back button). A simple refresh of that page shows the entry is ready for editing again.
My guess here would be that for whatever reason the browser is fetching the next page before the ajax request has been completely processed. The question is whether there is a way to ensure things happen in the correct order.
var fire_unload_ajax = true; // certain things set this to false, not relevant
$(window).unload(function() {
if(fire_unload_ajax && $("#reset-entry-lock form").length == 1) {
$.ajax({
url: window.location.href,
async: false,
cache: false,
type: "POST",
data: $("#reset-entry-lock form").serialize()
});
}
});
I solved a similar problem in an app I wrote recently.
I ended up with code in the load event of every page in my app that checked to see if the user still had locks and released them if they were no longer required. I also had code like yours that released locks on an unload event, in case the user was navigating to a page outside my app.
For Opera users (etc) I had intended to have locks automatically expire every minute or so, and do an async postback from my app every 30 seconds to renew the locks. That way, users who navigated elsewhere would have their locks freed even if the unload event was not fired. Unfortunately, my project was cancelled before this was implemented.
You could send the state of the form using an asynchronous AJAX POST every five minutes or so, like Gmail does when writing a message. As I explained in my comment, you can't rely on onbeforeunload.