This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 8 years ago.
I am looking for something like this
function someFunc() {
callAjaxfunc(); //may have multiple ajax calls in this function
someWait(); // some code which waits until async calls complete
console.log('Pass2');
}
function callAjaxfunc() {
//All ajax calls called here
console.log('Pass1');
}
What I have tried?
1
Jquery.when()
tried using it..it works fine. But not the way I want. $.when will wait but the code next to $.when() runs with out waiting. The code inside do callback only runs after ajax calls
2.
setTimeOut() with a global flag
I was so confident this will work. I tried like following.
GlobalFlag = false;
function someFunc()
callAjaxfunc(); //may have multiple ajax calls in this function
setTimeOut(waitFunc, 100); // some which waits until async calls complete
console.log('Pass2');
}
function callAjaxfunc() {
//All ajax calls called here
onAjaxSuccess: function() {
GlobalFlag = true;
};
console.log('Pass1');
}
function waitFunc() {
if (!GlobalFlag) {
setTimeOut(waitFunc, 100);
}
}​
Still not able to get wanted result. Am I doing something wrong here? This is not the way?
Result I wanted should come like this
Pass1
Pass2
Not able to make any fiddle as it needs AJAX calls
EDIT: As many were suggesting callbacks..i know about them..but still the code next to somewait() will get executed...I want browser to completely stop executing code next to somewait() until the ajax call..Also it may be a bad practice but worth to know and try if possible...
Use callbacks. Something like this should work based on your sample code.
function someFunc() {
callAjaxfunc(function() {
console.log('Pass2');
});
}
function callAjaxfunc(callback) {
//All ajax calls called here
onAjaxSuccess: function() {
callback();
};
console.log('Pass1');
}
This will print Pass1 immediately (assuming ajax request takes atleast a few microseconds), then print Pass2 when the onAjaxSuccess is executed.
Why didn't it work for you using Deferred Objects? Unless I misunderstood something this may work for you.
/* AJAX success handler */
var echo = function() {
console.log('Pass1');
};
var pass = function() {
$.when(
/* AJAX requests */
$.post("/echo/json/", { delay: 1 }, echo),
$.post("/echo/json/", { delay: 2 }, echo),
$.post("/echo/json/", { delay: 3 }, echo)
).then(function() {
/* Run after all AJAX */
console.log('Pass2');
});
};​
See it here.
UPDATE
Based on your input it seems what your quickest alternative is to use synchronous requests. You can set the property async to false in your $.ajax requests to make them blocking. This will hang your browser until the request is finished though.
Notice I don't recommend this and I still consider you should fix your code in an event-based workflow to not depend on it.
Real programmers do it with semaphores.
Have a variable set to 0. Increment it before each AJAX call. Decrement it in each success handler, and test for 0. If it is, you're done.
If you need wait until the ajax call is completed all do you need is make your call synchronously.
Related
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 6 years ago.
I'm having the hardest time wrapping my head around javascript callbacks for some reason and it's causing me a lot of headaches. I have three functions that I'm trying to use to parse some data from a website like so—
function parseText() {
//this finds a bunch of DOM elements, grabs the innerHTML from each and stores
//it in an array
}
function scrollToTop() {
//this scrolls to the top of the window where I'm getting text from,
//which causes the window to load more DOM elements to parse
}
function removeOldElements() {
//this removes the already-parsed DOM elements from the DOM
}
and I've been calling it like this.. which I now realise is a completely horrid method of doing things because as soon as I switch tabs Chrome completely messes with setTimeout and setInterval timings and causes a lot of errors..
function doEverything() {
parseText();
setTimeout(scrollToTop, 2000);
setTimeout(removeOldElements, 4000);
}
setInterval(doEverything, 5000);
//this loops it every 5 seconds so I can just run it in a separate tab
//while I continue to work on other things
This works kind of.. but any pause or interruption in setInterval breaks the code, and I know I should be using callbacks for this kind of thing in order to call one once the first one is done executing, but I just can't seem to get it to work.
I've been reading about callbacks and don't really understand what the format is supposed to be.. I've tried something like this:
function parseText(callback) {
}
function scrollToTop(callback) {
}
function removeOldElements() {
}
function doEverything() {
parseText(
scrollToTop(
removeOldElements
)
)
}
setInterval(doEverything, 5000);
But this only seems to be calling scrollToTop and then parseText twice.. and doesn't call the third function at all! What the heck! Now I'm really confused..
Could anyone assist? I'm sure I'm doing something very basic completely wrong here..
You're talking about callbacks, but I don't see anything explicitly async about your code. You need to differentiate between two things here:
Synchronous function calls: where the main thread executes the whole function block and never returns control until it's all finished executing. You don't need a callback for this sort of thing, you just call your function in-line.
// some code
func()
// some more code
`
Asynchronous functions, which need some time to execute. In order not to block the main thread (usually the UI thread itself), the code is deferred till some time later when the engine can spare some processing cycles. This is where you need callbacks. It looks like this:
// some code
async_func(callback_func)
// some more code
There is no guarantee that all the code inside async_func will execute before some more code is. In fact, it will most probably execute later.
From the names of your functions, it doesn't look like any of them is doing any actual async work. So you can just call them like so:
function doEverything() {
parseText()
scrollToTop()
removeOldElements()
}
In addition, you forgot the parentheses for the last function call removeoldElements(), that's why it didn't execute.
Callback is good choice, this example may guide you further.
function one(fn) {
console.debug('one...');
setTimeout(fn, 1000);
}
function two(fn) {
console.debug('two...');
setTimeout(fn, 1000);
}
function three(fn) {
console.debug('three...');
setTimeout(fn, 1000);
}
function loop() {
console.debug('loop...');
setTimeout(function() {
one(function() {
two(function() {
three(loop);
});
});
}, 1000);
}
setTimeout(loop, 1000);
Open browser console, for logs.
I'm not sure what you are trying to do, but I suggest to you doing this:
function parseText(callback) {
//some code here
console.log("parsetext");
scrollToTop('callback');
}
function scrollToTop(callback) {
//some code here
console.log("scrollToTop");
removeOldElements();
}
function removeOldElements() {
//some code here
console.log("removeOldElements");
setTimeout(parseText, 5000);
}
ingparseText();
I have three ajax calls which can run concurrently. However I want the browser to wait until all ajax calls completed. One way is to set async as false for each of them but then I lose precious time since they can run concurrently. Is there a way to run them concurrently but make the browser hangs until all completed?
After success, call callback function:
$.ajax({
success: function(){ checkAjax(); }
});
And in callback function count how much responses you have.
var totalEnded = 0, totalAjax = 3;
function checkAjax() {
totalEnded++;
if (totalEnded == totalAjax) {
/* Do important stuff here, or call another function */
}
}
If you are using jQuery then have a look at $.when, you can provide a success callback which gets executed when all the requests are finished loading.
To make work with asynchronous stuff easier, you can use the promises pattern. If you're on jQuery, you can use jQuery.when(). From the docs:
$.when( $.ajax( "/p1.php" ), $.ajax( "/p2.php" )).done(function( a1, a2 ) {
//do this when both ajax calls are done
});
You could do this using deferred objects, because ajax itself returns that object. Please refer to Pass in an array of Deferreds to $.when()
var deferreds = [
$.post(),
$.get(),
];
$.when.apply($, deferreds).done(function() {
// All ajax requests was done
});
But for some reason, when i put variable data into naughty, it does not have content of data.
function cookie() {
var naughty = 'aaa';
$.post('/cookie', function(data) {
naughty = data;
});
alert(naughty); // sends "aaa"
}
Why?
UPDATE: thank you! now i got it, its because $.post() is async, so alert(naughty) will be executed before it will be filled with data.
Now i have sub-question: when i turn off async, will only function cookie() postpone execution or whole browser engine will freeze until AJAX is done?
UPDATE 2: Yes, #Kevin B, you are right, naughty is never filled... i did mistake in first code sample. I am lucky that all answers are valid anyway with only second code sample :-)
As #Kevin B pointed out, async=false will be depreciated in jQuery 1.8. And it freezes browser until request is done...
sub-question #2: i have more code inside cookie(), after this AJAX request. So, can i somehow pause execution of cookie() untill AJAX is done, without freezing whole browser with async=false? Or do i have to find new way (using complete/success callback) of coding this idea?
LAST EDIT:
I realized that what i want is ASYNC:FALSE... So i simply used it. BTW. ASYNC:FALSE will NOT be removed from jQuery! Only special kind of use-case will be...
It's a race condition. As .post() is an asynchronous function, the alert executes before naughty gets reset to data.
If you want the response to come back before continuing, you can use .ajax and set async to false.
How can I stop $.post in jQuery working asynchronously?
Just do this:
function cookie() {
var naughty = 'aaa';
$.post('/cookie', function(data) {
naughty = data;
callNaughty(naughty);
});
}
function callNaughty(naughty) {
alert(naughty);
}
OR
function cookie() {
var naughty = 'aaa';
$.post('/cookie').done(function(data) {
naughty = data;
alert(naughty)
});
}
As $.post() is asynchronous, so naughty will update after response arrived.
Note
async: false is deprecated in jQuery 1.8
This is because $.post is asynchronous. Execution leaves that line immediately while page is being requested and function(data) is yet to be called so data is empty. The empty data is what alert picks up. This can only work after the request has completed e.g. in synchronous fashion in which the alert statement has to wait
Since $.post it's an ajax request, it's asynchronous by nature, so what you'd want to do it's to make sure that after you ajax request has been completed, then do whatever you want:
function cookie() {
var naughty ;
$.post('/cookie', function(data) {
naughty = data;
}).done(function(){
alert(naughty);
});
}
My guess would be that $.post is working asynchronously. So your alert is being called before the post callback has been executed. If you place the alert in the post callback you should get the correct result.
Is there an event in javascript that I could bind some sort of listener to that will tell me when all javascript/jQuery/Ajax is done executing on the page? The page will not be loading/unloading/reloading, etc between the time the execution begins and the time that I need the listener to "listen", so those events don't work. The page literally is not doing anything. The button is clicked and some javascript functions fire which contain Ajax calls to web services. After all have finished, I want to change window.location. But window.location is changing before the web services have finished in my case.
Currently using setTimeout to achieve this, but as sometimes the code needs more time to run than normal, sometimes the window.location is firing before all the other javascript has finished. Simply put
<input type = "button"... onclick="doThis();";
function doThis() {
try{
//Contains AJAX calls to web services which is mainly what screws up my timing since it may still be trying to execute stuff when the redirect statement happens
}
catch (e) {
}
//Currently doing setTimeout(redirect, 10000);
//Would like to simply detect when all of the above is done and then redirect.
}
Edit: Left out a crucial piece of info. The AJAX calls are in a for loop. The use of variables and success callbacks hasn't been working so well for me as by the time my success callback is executing, my variables have taken on new values in the for loop.
What you are trying to achieve is a classical concurrent programming problem. It is solved by the use of a barrier.
To put it simply, you need to:
Count how many calls you've done.
Set a callback on all AJAX completion events.
Make that callback decrement the number of calls.
The callback checks whether the number of calls has reached zero or not. If yes, then your final code (here, redirect) is called.
The actual implementation is left as an exercise to the reader :)
Hint: embed AJAX calls into a function that handles all counter incrementation and callback setting.
What I do:
Create a variable that represents the number of outstanding AJAX calls.
Before making an AJAX call, increment the variable.
At the end of the code that completes an AJAX call, call a function (e.g. ajaxComplete).
ajaxComplete should decrement the count. When it reaches zero, you know all your calls are complete.
Assuming you're using jQuery.ajax, it sounds like you're looking for ajaxStop.
Why don't you try using something like the Underscore library's after function in the callbacks?
var done = _.after(3, function() {
window.location = 'http://example.com';
});
$.ajax({
url: '/tic',
success: function() {
done();
}
});
$.ajax({
url: '/tac',
success: function() {
done();
}
});
$.ajax({
url: '/toe',
success: function( data ) {
done();
}
});
You should check for the response from AJAX call, and only in that response do redirect. This way you will avoid doing redirect while AJAX was still executing.
I have a simple Javascript function:
makeRequest();
It does a bunch of stuff and places a bunch of content into the DOM.
I make a few calls like so:
makeRequest('food');
makeRequest('shopping');
However, they both fire so quickly that they are stepping on each other's toes. Ultimately I need it to have the functionality of.
makeRequest('food');
wait....
makeRequest('shopping'); only if makeRequest('food') has finished
Thoughts on getting these to execute only one at a time?
Thanks!
If these functions actually do an AJAX request, you are better keeping them asynchronous. You can make a synchronous AJAX request but it will stop the browser from responding and lead to bad user experience.
If what you require if that these AJAX requests are made one after the other because they depend on each other, you should investigate your function to see if it provides a callback mechanism.
makeRequest('food', function()
{
// called when food request is done
makeRequest('shopping');
});
Using jQuery, it looks something like that
$.get("/food", function(food)
{
// do something with food
$.get("/shopping", function(shopping)
{
// do something with shopping
});
});
I would recommend that you simply write them asynchronously--for example, call makeRequest('shopping'); from the AJAX completion handler of the first call.
If you do not want to write your code asynchronously, see Javascript Strands
I suppose that you have a callback method that takes care of the response for the request? Once it has done that, let it make the next request.
Declare an array for the queue, and a flag to keep track of the status:
var queue = [], requestRunning = false;
In the makeRequest method:
if (requestRunning) {
queue.push(requestParameter);
} else {
requestRunning = true;
// do the request
}
In the callback method, after taking care of the response:
if (queue.length > 0) {
var requestParameter = queue.splice(0,1)[0];
// do the request
} else {
requestRunning = false;
}