let's say I'm doing 3 ajax calls and I want to wait for the 3 calls to finish before doing something.
Is there a library out there to synchronize multiple async events in JavaScript ? (using or not jQuery's event system)
Ex.:
var sync = new syncLib();
$('a1').click(sync.newListener());
$('a2').click(sync.newListener());
sync.wait(function(e1, e2) {
// fired when both a1 and a2 are clicked or when 10 seconds have passed
// e1 and e2 would have properties to know whether or not they timed out or not..
}, 10 /* timeout */));
I have found this one: https://github.com/Ovea/js-sync/blob/master/README.md, but timeouts are not supported. (Let's say the second ajax call takes too long, I don't want my synchronization to be hung up, I want to set a 10 secs timeout)
I know I can code something myself, but I'm just checking here (after googling for it)
Thanks!
EDIT:
Since then I found async: https://github.com/caolan/async
$.when($.ajax("/"), $.ajax("/"), $.ajax("/")).then(function () {
alert("all 3 requests complete");
});
Documentation
you can use jquery deferred object
here is a useful post http://www.erichynds.com/jquery/using-deferreds-in-jquery/
The .deferred, .when, .then solution mentioned in other answers is much more elegant, but it's also possible write your own simple solution just so you see how this can be done manually. You just set a counter for how many ajax calls you have in flight and in the success handler for each ajax calls, you decrement the counter and fire your action when the counter gets to zero.
function DoMyAjaxCalls(callbackWhenDone) {
var numAjaxCalls = 3;
// set timeout so we don't wait more than 10 seconds to fire the callback
// even if ajax calls aren't done yet
var timer = setTimeout(callbackWhenDone, 10*1000);
function checkAjaxDone() {
--numAjaxCalls;
if (numAjaxCalls == 0) {
clearTimeout(timer);
callbackWhenDone();
}
}
// first ajax call
$.ajax({
url: 'ajax/test1.html',
success: function(data) {
// write code to handle the success function
checkAjaxDone();
},
error: checkAjaxDone
});
// second ajax call
$.ajax({
url: 'ajax/test2.html',
success: function(data) {
// write code to handle the success function
checkAjaxDone();
},
error: checkAjaxDone
});
// third ajax call
$.ajax({
url: 'ajax/test3.html',
success: function(data) {
// write code to handle the success function
checkAjaxDone();
},
error: checkAjaxDone
});
}
Here you have a [library][1] based on jQuery made for that purpose.
In simple use-cases, $.when() is BEST but jcon-q-rency allows you to synchronize any asynchronous code sections.
http://www.megiddo.ch/jcon-q-rency
Related
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
});
I have an Ajax request waiting for response from another process.
function test() {
var flag = 0;
while (flag === 0) {
$.ajax({
url: "cs/CheckForProcess",
async: false,
success: function(data) {
if (data !== 'NotReady') {
$('#results').html(data);
flag = 1;
} else {
$('#results').html('<h1>Processing...</h1>');
setTimeout(function() {
}, 6000);
}
}
});
}
}
the problem is that the setTimout isnt working although i see in debug mode that the else condition is executed.
EDIT:
i want the next ajax request to be sent only 6 seconds after validating the process is not ready.
what am i missing?
Thx.
setTimeout is an async function. It does not pause your script. Your script will continue to run (and 6000ms later your setTimeout callback function will execute).
You could consider using setInterval to keep checking for your other condition to be true.
You could probably remove async:false by keeping track of your server response elsewhere. Once you have a successful callback then you should cancel the interval.
function test() {
var timerId = 0,
timerId = setInterval(function(){
$.ajax({
url: "cs/CheckForProcess",
success: function(data) {
if (data !== 'NotReady') {
$('#results').html(data);
clearInterval(timerId);
}
}
});
}, 6000)
}
Javascript does not have the ability to sleep (e.g. suspend or block javascript execution) using setTimeout.
A setTimeout() schedules a function to run sometime in the future and other javascript just happily keeps running during that time.
What you should do is use setTimeout() to schedule the next run of your ajax function if the data was not yet ready. So, when there is no data ready, you schedule the next ajax call for 6 seconds from now, but when you do get the data, you just process the data and you're done.
In addition, you really don't want to use async: false because that freezes the browser (doesn't allow any other processing to occur) during the ajax call. This same operation can be written to leverage the asynchronous nature of ajax and allow you to still solve your problem, but allow other processing to continue in the browser with no browser blocking. This requires asynchronous programming techniques.
You can do so like this:
function test() {
function runIt() {
$.ajax({
url: "cs/CheckForProcess",
async: true,
success: function(data) {
if (data !== 'NotReady') {
$('#results').html(data);
} else {
// if data not ready yet, then put up some progress
// and call this again in 6 seconds
$('#results').html('<h1>Processing...</h1>');
setTimeout(runIt, 6000);
}
}
});
}
// start the first iteration
runIt();
}
The setTimeout function takes a function to execute after the timeout. You are passing an empty function. So, after 6 seconds, that empty function is executing - and obviously not doing anything.
I think you are expecting setTimeout to delay the execution of other scripts on your page. This is not the case, as setTimeout delays and executes the supplied callback function in a new thread (not blocking other execution on your page).
If you are trying to do something after 6 seconds, just supply the code inside of the function() {} code you have. Eg:
setTimeout(function() {
$.ajax(...);
}, 6000);
or
setTimeout(runIt, 6000);
Links:
setTimeout documentation (mozilla)
Related SO question
I was following a video tutorial on how to do long polling, and it seems I got it done, but I'm having one issue, for some reason, though I get one response from server, my alert shows up 3-6 times. I thought "success" only happens once if we got one response.
2nd question is, how can I make this javascript code cancel the ajax call every 30 seconds, and restart it? I've put in setInterval with .abort() in there while experimenting with no luck, probably wrong placement.
Thank you for the wisdom and help!
var timestamp = null;
var imp = null;
var annk = null;
function waitForMsg(){
$.ajax(
{
type: "GET",
url: "/test?timestamp=" + timestamp + "&imp=" +imp + "&annk=" +annk,
dataType : 'json',
async: true,
cache: false,
success: function(data)
{
alert("hello");
if(data.annkr > "0"){
$("#myidone").css("background-color", "#cccccc");
}else{
$("#myidone").css("background-color", "#cccccc");
}
if(data.impr > 0){
$("#myidtwo").css("background-color", "#000000");
}else{
$("#myidtwo").css("background-color", "#000000");
}
annk = data.annkr;
imp = data.impr;
timestamp = data.timestamp;
setTimeout('waitForMsg()',2000);
}
});
}
$(document).ready(function(){
waitForMsg();
});
I read stuff on Stackoverflow about readystates, but how do I make sure it's ready only once and does it after it's ready?
If the code is exactly the one you posted, there's no reason to show the alert more than 1 and then after 2s for the rescheduling.
Can you make a jsFiddle for that showing the problem?
The second question is more interesting. You can use the timeout option in the .ajax call and then, in the error handler, just reschedule the call.
My personal suggestion is to refactor your code to use the new JQuery Ajax style base on .done .fail and .always.
And that setTimeout should be written as setTimeout(waitForMsg,2000). Using the string parameter you evaluate that string instead of just calling the function and it's a performance penalty (so small that's hardly noticeable but that is ).
You might be seeing the alert multiple times due to the setTimeout() in your success function, you keep calling the waitForMsg() function. Just a guess.
To abort the request you could do something like this:
var timer = null;
function waitForMessage() {
var req = $.ajax(YOUR_CODE);
// The 30 second timeout
timer = setTimeout(function() {
req.abort();
waitForMessage();
}, 30000);
}
Or slightly better maybe:
function waitForMsg() {
$.ajax({
timeout: 30000,
error: function(err) {
if(err === 'timeout') {
waitForMsg();
}
}
})
}
Actually you should use setInterval instead of setTimeout, using this approach if your ajax call fails, you will forcelly abort the loop.
You could also, cancel or change the timeout timings depending on the usage of your service for network resource sanity.
Consider I have multiple (sometimes more than 12) ajax calls that are calling every 2 seconds or more. Data gathered through the calls are set to the UI contained elements (Like progress bars). After all I have delay on SCROLL while timers working . This delay is natural, But How can I handle it?
NOTE: Calls Destinations are services that provides data with the minimum spent time. The point that makes the scroll sad, is using multiple setTimeout() and setInterval() methods. To get more familiar with my work, See the below code:
function FillData(accessUrl, name) {
var add = accessUrl;
$.support.cors = true;
if (add) {
$.ajax({
type: 'GET',
url: accessUrl,
crossDomain: true,
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function (data) {
Update(name, data);
},
error: function (xhr, status, error) {
LogResponseErrors(status , error, name);
}
});
setTimeout(function () { FillData(accessUrl, name); }, interval);
//Consider that the method calls with different parameters one time and it will run automatically with setTimeout
}
else {
freezeFrame(name);
}
}
Used Tags explains what I used.
Any useful answer will be appreciated
From what I understand in your question. You have delay when you're handling your ajax responses and you need to remove the delay.
Javascript is single-threaded. Therefore, if there is a function that takes long time to complete, it could dominate the thread and cause the UI not responding. To deal with this, you have 2 options:
Optimize your code so that the function does not take long.
Use setTimeout to break your function into smaller pieces. For example: if your function is executing a loop of 100 items, you could break it to execute 10 times with 10 items each.
Update: (based on updated question):
It seems that the loop never stops when you use setTimeout like this. Should have something like:
counter++;
if (counter <= 12)
setTimeout(function () { FillData(accessUrl, name); }, interval);
Due to timing problem between ajax and your setTimeout, at some points, there are a lot of events (escalated) waiting in the queue to be executed and cause performance problem. Try putting your setTimeout inside your success or complete function
I use setInterval and sometimes it happens "too fast". Here how it looks:
setInterval(function() {
//here comes ajax functions and so on.
}, 1000);
Sometimes setInterval happens faster than all those ajax functions and it gives me two messages instead of one. What a solution to this?
It's hard to tell what you're running into, the question is a bit unclear.
setInterval is great for some things, but not for anything where you're going to be mixing other asynchronous stuff in with it. Instead, use the "rescheduling setTimeout" idiom:
setTimeout(doSomething, 1000);
function doSomething() {
$.ajax("your_url", {
success: function() {
// Do something here
// Do something else here
},
complete: function() {
// Previous run complete, schedule the next run
setTimeout(doSomething, 1000);
}
});
}
...because, after all, your ajax call may take more than a second to complete.
If that's not the problem you're having, my guess is your code looks something like this:
setInterval(function() {
$.ajax("your_url", {
success: function() {
// Do something here
}
});
// Do something else here
}, 1000);
...and you're wondering why the "Do something else here" code is running before the "Do something here" code. If so, the reason is that by default, ajax calls are asynchronous. Your call to $.ajax starts the call, but that's all; then all your other code runs before the success (or error) callbacks occur.
The fix, of course, is to not do anything else at the top level that relies on the success callback:
setInterval(function() {
$.ajax("your_url", {
success: function() {
// Do something here
// Do something else here
}
});
}, 1000);
With jQuery 1.5.x you can use the Then() for deferred object. This is a nice way to say once you are done then() do this. You can also use the When() option to have it wait for more than one ajax request to complete.
These two things are very cool and powerful.
http://api.jquery.com/deferred.then/
http://api.jquery.com/jQuery.when/
Set a flag that indicates that the ajax fetches are in process. When all of the ajax fetches complete, clear the flag. At the top of your setInterval function, return immediately if the flag is set.
It's better not to use setInterval, but to set a fresh setTimeout each time. For example:
setTimeout(function ajaxStuff() {
// here comes ajax functions and so on.
setTimeout(ajaxStuff, 1000);
}, 1000);
Of course, if the functions within are asynchronous, as AJAX requests normally are, the setTimeout call will still come too soon. You'll need to write some code that calls setTimeout when the requests are complete. $.when helps you with this, since $.ajax and other jQuery AJAX methods implement $.Deferred:
setTimeout(function ajaxStuff() {
$.when(
$.ajax({
url: 'ajax1.htm'
}),
$.ajax({
url: 'ajax2.htm'
}),
$.ajax({
url: 'ajax3.htm'
})
).done(function() {
setTimeout(ajaxStuff, 1000);
});
}, 1000);
I think the problem here is due to scope. Eventhough the method is triggered successfully.
With similar problem I have able to use this to fix:
setTimeout(function(){
load1();
}, 5000);
function load1 () {
console.log('loaddd1..');
setTimeout(load2(), 4000);
}
function load2 () {
setTimeout(function(){
console.log('end load2');
}, 4000);
had this issue and clearInterval wasn't working.
make sure setInterval is only called once by wrapping it in an if statement:
var interval;
if (typeof(interval) === 'undefined') {
interval = setInterval(actFakeData,3000);
}
also helpful for me was assigning setInterval to a variable and console.log it so you can see the value throughout your code. for me when it was speeding up it was increasing in numeric value instead of resetting until I wrapped it in this.