Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I am using following JS code to call a web handler.
This code calls the handler perfectly JUST IN IE and not FF.
$.ajax({ type: "GET",
url: "../MasterPages/AHMHandler.ashx?T=1",
dataType: "HTML",
success: function (msg) {
document.getElementsByName('cartId')[0].value = msg;
}
,
error: function (e) {
return false;
}
});
Sleep(2000);
What is the problem with my code?
Having seen the Sleep() call in your code, and your comment about alert(), I would say that your problem is with a lack of understanding of how Ajax code works.
When you mak an Ajax call, it is called asynchronously. This means that the call is made, and then the rest of your current function carries on running without stopping to wait for the ajax code to run.
The ajax success function will be called eventually, but only when the http request is complete. In the meanwhile, your current function will carry on running.
The point here is that you cannot rely on a given sequence of events if you have code in the same function that runs after the ajax call is made.
Putting a Sleep() there might make it appear to work because some browsers might see the sleeping time as an opportunity to run the ajax success function, so your code seems to run in the right order. Putting an alert() there will be even more likely to make it work, because the alert() will generally take more time before it is cleared, so the ajax function has more chance to run.
But you should not rely on either of them to get your execution sequence right.
What you should do instead is put the code that you want to run after the ajax call inside the success function. This is the only way to be sure that it will be run after the ajax call is finished.
Hope that helps.
[EDIT] Further clarification after OP's comment:
Spudley, Sleep Function is my own function to keep browser from being redirect for like 2 secs. function Sleep(milliseconds) { var start = new Date().getTime(); for (var i = 0; i < 1e7; i++) { if ((new Date().getTime() - start) > milliseconds) { break; } } }
Just for ref, a sleep function like that is a really terrible idea in Javascript. You should use a setTimeout() for that kind of thing.
However, the point is still the same -- you have code that runs after the $.ajax(), and it will be blocking execution of your ajax success function. If you're doing a redirect right afterward, then the success function probably never gets a chance to run.
An alert() would indeed make it work that because the success function will find a slot to run when the alert is cleared, before Sleep is called, but you shouldn't rely on that.
The answer remains the same: You should put the code that you want to run after the ajax call inside the success function.
Here's a your code with the changes made:
$.ajax({ type: "GET",
url: "../MasterPages/AHMHandler.ashx?T=1",
dataType: "HTML",
success: function (msg) {
document.getElementsByName('cartId')[0].value = msg;
setTimeout(function() { //this instead of your Sleep function
//this is where you need to do your redirect, or whatever else you're doing after the ajax completes.
}, 2000);
}
,
error: function (e) {
return false;
}
});
//don't put **any** code here after the ajax call! put it in the success function.
Related
This question already has answers here:
Easy to understand definition of "asynchronous event"? [closed]
(11 answers)
Closed 7 years ago.
I have the following Javascript and it's returning results out of the order I would expect them.
function getCurrentItems(id){
alert("2");
$.ajax({
url: "somePHPurlthatspitsoutajsonencodedArray.php",
type: "POST",
dataType: "json'",
data: {id:id},
success: function(data){
alert("3");
}
});
alert("4");
}
$(document).on('click', '.eventClass', function(e){
alert("1");
var id = "someID";
var results = getCurrentItems(id);
alert("5");
});
I would think I'd get alerts in the order of 1, 2, 3, 4, 5.
Instead I get them in the order of 1, 2, 4, 5, 3.
I just can't figure out why that success alert (5) fires last?
AJAX is short for asynchronous JavaScript and XML. Asynchronous is the keyword here. I'll use an allegory to explain async.
Lets say you're washing dishes manually. You can't do anything else while doing that, so it is synchronous.
Lets say you put your dishes in a dishwasher. You can do other things, you've delegated the task to the dishwasher, so this is asynchronous. Asynchronous is generally better, because you can do multiple things at one time. Javascript only asyncs when requesting info from the server, as Javascript is single threaded (it only runs on 1 CPU core and can only do one thing at a time on it's own).
A callback is a function that is called when the async task completes. The dishwasher finishes, so now you have to empty it as soon as you complete what you're doing when it finshed.
So in your code, you start the dishwasher, say you're going to alert("3") when the dishwasher finishes running, and then you go alert 4 and 5. Then when the dishwasher finishes/your server returns data, you alert 3 like you said you would.
That make sense?
The reason that you give a success callback function in an ajax request is that the request might take some time to return, but your code can continue running. In this case the ajax request is sent, and the success function is remembered for later, then your code continues (alerting 4 and 5). When the browser receives the response to the ajax request it calls the success function (alerting 3).
The way that Javascript works, only one thread is run per browser tab, so event handlers are not called until any previously running code is completed. Therefore, even if your getCurrentItems function were to take several seconds to complete, by which time the server response had returned, the success function would still not be called until after your function had completed.
Edit: Because an ajax call can take some time, it is not generally desirable to be able to call a function like getCurrentItems which includes an ajax request, and wait for the response. If you do this, then you are likely to leave your browser window unresponsive, and you will have to deal with potential errors from the ajax call. Instead, you should put any code that you wish to run which relies on the ajax result in the success function, or in another function which you call from there. As languages go, Javascript is very good for doing this kind of thing. You could even take a callback function as an argument to getCurrentItems, and run it from the success function.
As others have mentioned, you're getting the results in the order you see because the ajax request doesn't return with it's data and call your success callback until the response is received, but code continues to execute immediately after the $.ajax call.
To get the workflow you're probably expecting, you could use promises, or simply pass in a callback:
function getCurrentItems(id, onSuccess){
alert("2");
$.ajax({
url: "somePHPurlthatspitsoutajsonencodedArray.php",
type: "POST",
dataType: "json'",
data: {id:id},
success: function(data){
alert("3");
onSuccess(data);
}
});
alert("4");
}
$(document).on('click', '.eventClass', function(e){
alert("1");
var id = "someID";
var results = getCurrentItems(id, function(data){
console.log('data',data);
alert("5");
});
});
This question already has an answer here:
Run function if jQuery.ajax waiting for respond long enough
(1 answer)
Closed 8 years ago.
I know about the timeout setting for the ajax call. But what i'm wondering is, is there a way to display a message to the user if an ajax call is still processing but taking longer than x seconds.
E.g.
During an ajax call, if it takes longer than 10 secs tell the user, "call taking longer than expected"
I'd say your best bet is to use window.setTimeout for however long you want to wait for before showing your notification, and then add a window.clearTimeout line to your success callback in your $.ajax() call:
var loadingTimeout = window.setTimeout(function() {
// show your warning here
alert('Still loading :P');
}, 10000); // 10000ms = 10sec
$.ajax({
url: 'http://your/url/here',
dataType: 'json',
type: 'GET',
success: function(r) {
window.clearTimeout(loadingTimeout);
// your results logic here
}
})
Sure, just setTimeout() yourself another function that checks some global variable that gets set by the ajax completion callback. In that function, if the ajax call is still outstanding, show a message.
Here is the code :-
var quiz;
function startQuiz() {
$.ajax({
url: 'get_quiz/',
cache: 'false',
dataType: 'json',
async: 'false',
success: function(data) {
quiz = data;
alert(quiz[0].q); // I'm able to access quiz here
},
}
);
}
startQuiz();
alert(quiz[0].q); // Not able to access it here.
I'm not able to access quiz here, am I mission something?, Whats wrong with this?
Ajax is assynchronous which can be an unfamiliar concept. Your code will run like this:
1. var quiz;
2. define function startQuiz;
3. call startQuiz;
4. do ajax call (and continue! don't block)
5. alert(quiz[0].q); // Not able to access it here.
-- ajax call comes back
6. quiz = data;
7. alert(quiz[0].q); // I'm able to access quiz here
Ajax is asynchronous, it doesn't block. This means that when you make the ajax call the callback doesn't actually get called until the ajax call returns, it doesn't block and wait. Instead the code will continue on.
Then later when the ajax call returns the data, your callback function will be executed.
Javascript does this by means of an event loop.
See it like this: steps 1-5 are part of the first event. 6-7 are part of the second event.
A cool thing about JavaScript is that in your callback you still have access to anything above it (like the variable quiz) because of scoping. This is called a closure. Your callback function closes around the scope and brings it with him to the next event.
AJAX calls are asynchronous, you should wait for the result to come back from the server. Either do all the work in a callback function or have a look on a promises library (I like Q promises library), which makes waiting for AJAX results very easy.
This is due to the asynchronous nature of JavaScript, once you call startQuiz() it executes and jumps back out and executes your quiz alert().
You have to access your quiz data explicitly after the callback is called to make sure you have access to it.
You also have to worry about scoping, as you may not be actually modifying the same quiz variable.
var quiz,
fetched = false;
$.ajax({
//blah
success : function(data){
fetched = true;
quiz = data;
}
});
setInterval(function(){
if(fetched){
//quiz is populated
}else{
//quiz hasn't be populated yet
}
},50);
Although not a clever example, I'm just trying to get the point across that startQuiz() doesn't wait for the ajax call. The A in AJAX mean asynchronous.
You should give jQuery deferreds a try which ist the preferred way of writing ajax related code since jQuery 1.5: http://javascriptplayground.com/blog/2012/04/jquery-deferreds-tutorial
Once you get the hang of it maybe will be easier to write and understand your code.
I'd like to preface this with an apology if I'm doing things in a "weird" way, as I'm primarily a C developer and am solving this AJAX problem the way I would in C.
I have a script that will be connecting to a "push server" that waits until a message is available, then sends only that one message and breaks the connection. The client must then reestablish the connection to listen for future messages.
I tried to do this by implementing a synchronous AJAX call within an asynchronous callback, and it works except it appears the DOM (maybe? I'm showing my ignorance of JS here) will block until all calls are complete.
I do not know how to do it with purely asynchronous calls as I do not want to end up exhausting the stack by having a callback calling a callback each time.
This is the code:
$.ajax({
url: './recoverDevice',
data: JSON.stringify(requestData),
dataType: 'json',
type: 'POST',
success: function(j)
{
console.log(j);
if (j.success)
{
//Indefinitely listen for push messages from the server
var loopMore = true;
while(loopMore)
{
$.ajax({
async: false,
url: './getPendingMessage',
dataType: 'json',
type: 'POST',
success: function(j)
{
//alert(j.message);
$("#progressBox").append("<li>" + j.message + "</li>");
loopMore = !j.complete;
}
});
}
}
else
{
$("#errorBox").show();
$("#errorBox").text(j.errorMessage);
}
}
});
Now, logically, this code should work. Within an asynchronous function, I loop over a synchronous JS call, each time I get a message I will append it to the DOM, and only when the server tells me there will be no more messages do I exit the loop, ending the asynchronous thread and completing the task.
The problem is that the DOM access appears to be all coalesced once all messages have been received. i.e. the appends only happen once all messages have been received and the asynchronous thread has exited.
The commented out alert was a test - it works perfectly. I get a message box after each and every notification, and it pauses correctly until the next message (with the rest of the code as-is).
I'm guessing this is my browser (Chrome) doing some magic to protect against race conditions by not allowing DOM manipulation until the asynchronous thread has exited? Or am I way off the mark and barking up the wrong tree here?
Getting rid of the loop and setting async to true makes the first message be received properly (no problems there), but obviously no messages thereafter.
Obviously I could do something like this:
function GetMessage()
{
$.ajax({
async: true,
url: './getPendingMessage',
dataType: 'json',
type: 'POST',
success: function(j)
{
$("#progressBox").append("<li>" + j.message + "</li>");
if (!j.complete)
{
GetMessage();
}
}
});
}
But that would result in a stack overflow over time (no?).
An obvious solution would be to use asynchronous calls here too, but to signal a while loop to pause and continue with new calls via some sort of synchronization primitives, but appears that JS does not have signalling primitives?
Figured this one out - I don't know why I didn't see this before but my latter code fragment works perfectly. I didn't realize it at the time of posting, but it can't overflow the stack because each time it runs it launches an async call and exits - so the stack frame is never more than 2 or 3 deep. The asynchronous calls are managed externally and won't be on the stack, so each time it starts over.
I'd still appreciate any input on why the first method (synchronous code in asynchronous call) didn't/wouldn't work.
Okay, so I appreciate that Javascript is not C# or PHP, but I keep coming back to an issue in Javascript - not with JS itself but my use of it.
I have a function:
function updateStatuses(){
showLoader() //show the 'loader.gif' in the UI
updateStatus('cron1'); //performs an ajax request to get the status of something
updateStatus('cron2');
updateStatus('cron3');
updateStatus('cronEmail');
updateStatus('cronHourly');
updateStatus('cronDaily');
hideLoader(); //hide the 'loader.gif' in the UI
}
Thing is, owing to Javascript's burning desire to jump ahead in the code, the loader never appears because the 'hideLoader' function runs straight after.
How can I fix this? Or in other words, how can I make a javascript function execute in the order I write it on the page...
The problem occurs because AJAX is in its nature asynchronus. This means that the updateStatus() calls are indeed executed in order but returns immediatly and the JS interpreter reaches hideLoader() before any data is retreived from the AJAX requests.
You should perform the hideLoader() on an event where the AJAX calls are finished.
You need to think of JavaScript as event based rather than procedural if you're doing AJAX programming. You have to wait until the first call completes before executing the second. The way to do that is to bind the second call to a callback that fires when the first is finished. Without knowing more about the inner workings of your AJAX library (hopefully you're using a library) I can't tell you how to do this, but it will probably look something like this:
showLoader();
updateStatus('cron1', function() {
updateStatus('cron2', function() {
updateStatus('cron3', function() {
updateStatus('cronEmail', function() {
updateStatus('cronHourly', function() {
updateStatus('cronDaily', funciton() { hideLoader(); })
})
})
})
})
})
});
The idea is, updateStatus takes its normal argument, plus a callback function to execute when it's finished. It's a reasonably common pattern to pass a function to run onComplete into a function which provides such a hook.
Update
If you're using jQuery, you can read up on $.ajax() here: http://api.jquery.com/jQuery.ajax/
Your code probably looks something like this:
function updateStatus(arg) {
// processing
$.ajax({
data : /* something */,
url : /* something */
});
// processing
}
You can modify your functions to take a callback as their second parameter with something like this:
function updateStatus(arg, onComplete) {
$.ajax({
data : /* something */,
url : /* something */,
complete : onComplete // called when AJAX transaction finishes
});
}
I thinks all you need to do is have this in your code:
async: false,
So your Ajax call would look like this:
jQuery.ajax({
type: "GET",
url: "something.html for example",
dataType: "html",
async: false,
context: document.body,
success: function(response){
//do stuff here
},
error: function() {
alert("Sorry, The requested property could not be found.");
}
});
Obviously some of this need to change for XML, JSON etc but the async: false, is the main point here which tell the JS engine to wait until the success call have returned (or failed depending) and then carry on.
Remember there is a downside to this, and thats that the entire page becomes unresponsive until the ajax returns!!! usually within milliseconds which is not a big deals but COULD take longer.
Hope this is the right answer and it helps you :)
We have something similar in one of our projects, and we solved it by using a counter. If you increase the counter for each call to updateStatus and decrease it in the AJAX request's response function (depends on the AJAX JavaScript library you're using.)
Once the counter reaches zero, all AJAX requests are completed and you can call hideLoader().
Here's a sample:
var loadCounter = 0;
function updateStatuses(){
updateStatus('cron1'); //performs an ajax request to get the status of something
updateStatus('cron2');
updateStatus('cron3');
updateStatus('cronEmail');
updateStatus('cronHourly');
updateStatus('cronDaily');
}
function updateStatus(what) {
loadCounter++;
//perform your AJAX call and set the response method to updateStatusCompleted()
}
function updateStatusCompleted() {
loadCounter--;
if (loadCounter <= 0)
hideLoader(); //hide the 'loader.gif' in the UI
}
This has nothing to do with the execution order of the code.
The reason that the loader image never shows, is that the UI doesn't update while your function is running. If you do changes in the UI, they don't appear until you exit the function and return control to the browser.
You can use a timeout after setting the image, giving the browser a chance to update the UI before starting rest of the code:
function updateStatuses(){
showLoader() //show the 'loader.gif' in the UI
// start a timeout that will start the rest of the code after the UI updates
window.setTimeout(function(){
updateStatus('cron1'); //performs an ajax request to get the status of something
updateStatus('cron2');
updateStatus('cron3');
updateStatus('cronEmail');
updateStatus('cronHourly');
updateStatus('cronDaily');
hideLoader(); //hide the 'loader.gif' in the UI
},0);
}
There is another factor that also can make your code appear to execute out of order. If your AJAX requests are asynchronous, the function won't wait for the responses. The function that takes care of the response will run when the browser receives the response. If you want to hide the loader image after the response has been received, you would have to do that when the last response handler function runs. As the responses doesn't have to arrive in the order that you sent the requests, you would need to count how many responses you got to know when the last one comes.
As others have pointed out, you don't want to do a synchronous operation. Embrace Async, that's what the A in AJAX stands for.
I would just like to mention an excellent analogy on sync v/s async. You can read the entire post on the GWT forum, I am just including the relevant analogies.
Imagine if you will ...
You are sitting on the couch watching
TV, and knowing that you are out of
beer, you ask your spouse to please
run down to the liquor store and
fetch you some. As soon as you see
your spouse walk out the front door,
you get up off the couch and trundle
into the kitchen and open the
fridge. To your surprise, there is no
beer!
Well of course there is no beer, your
spouse is still on the trip to the
liquor store. You've gotta wait until
[s]he returns before you can expect
to have a beer.
But, you say you want it synchronous? Imagine again ...
... spouse walks out the door ... now,
the entire world around you stops, you
don't get to breath, answer the
door, or finish watching your show
while [s]he runs across town to
fetch your beer. You just get to sit
there not moving a muscle, and
turning blue until you lose
consciousness ... waking up some
indefinite time later surrounded by
EMTs and a spouse saying oh, hey, I
got your beer.
That's exactly what happens when you insist on doing a synchronous server call.
Install Firebug, then add a line like this to each of showLoader, updateStatus and hideLoader:
Console.log("event logged");
You'll see listed in the console window the calls to your function, and they will be in order. The question, is what does your "updateStatus" method do?
Presumably it starts a background task, then returns, so you will reach the call to hideLoader before any of the background tasks finish. Your Ajax library probably has an "OnComplete" or "OnFinished" callback - call the following updateStatus from there.
move the updateStatus calls to another function. make a call setTimeout with the new function as a target.
if your ajax requests are asynchronous, you should have something to track which ones have completed. each callback method can set a "completed" flag somewhere for itself, and check to see if it's the last one to do so. if it is, then have it call hideLoader.
One of the best solutions for handling all async requests is the 'Promise'.
The Promise object represents the eventual completion (or failure) of an asynchronous operation.
Example:
let myFirstPromise = new Promise((resolve, reject) => {
// We call resolve(...) when what we were doing asynchronously was successful, and reject(...) when it failed.
// In this example, we use setTimeout(...) to simulate async code.
// In reality, you will probably be using something like XHR or an HTML5 API.
setTimeout(function(){
resolve("Success!"); // Yay! Everything went well!
}, 250);
});
myFirstPromise.then((successMessage) => {
// successMessage is whatever we passed in the resolve(...) function above.
// It doesn't have to be a string, but if it is only a succeed message, it probably will be.
console.log("Yay! " + successMessage);
});
Promise
If you have 3 async functions and expect to run in order, do as follows:
let FirstPromise = new Promise((resolve, reject) => {
FirstPromise.resolve("First!");
});
let SecondPromise = new Promise((resolve, reject) => {
});
let ThirdPromise = new Promise((resolve, reject) => {
});
FirstPromise.then((successMessage) => {
jQuery.ajax({
type: "type",
url: "url",
success: function(response){
console.log("First! ");
SecondPromise.resolve("Second!");
},
error: function() {
//handle your error
}
});
});
SecondPromise.then((successMessage) => {
jQuery.ajax({
type: "type",
url: "url",
success: function(response){
console.log("Second! ");
ThirdPromise.resolve("Third!");
},
error: function() {
//handle your error
}
});
});
ThirdPromise.then((successMessage) => {
jQuery.ajax({
type: "type",
url: "url",
success: function(response){
console.log("Third! ");
},
error: function() {
//handle your error
}
});
});
With this approach, you can handle all async operation as you wish.