I have a javascript function that calls an AJAX, like this:
function addSquadronByID(id) {
$.ajax
({
type: "POST",
url: "server/get_squadron.php",
data: {
'id': id,
'counter': squadron_counter
},
cache: false,
success: function (data) {
squadron_counter++;
},
error: function () {
alert("AJAX error.");
}
});
}
}
Outside the document.ready, the variable is initialized like this var squadron_counter = 0;
This function perfectly works while I call it in the page, but if I try to use PHP to write it in the page, like this:
$list_squadrons = $DB->Execute($query);
while(!$list_squadrons->EOF){
$currentSquadron_id = $list_squadrons->fields["squadron_id"];
$currentSquadron_number = $list_squadrons->fields["squadrons"];
echo "addSquadronByID($currentSquadron_id);\n";
$list_squadrons->MoveNext();
}
The function writes into the document.ready() the correct calls, but squadron_counter is always zero, even if the function works. My only idea is that it works this way because javascript calls all the functions at once and does not wait to complete the first one before executing the second one, etc.. but how do I solve this?
HTML output as requested:
addSquadronByID(3, squadron_counter);
addSquadronByID(5, squadron_counter);
addSquadronByID(6, squadron_counter);
This is put into a
$( document ).ready(function() {
});
inside a <script> tag.
I think your idea about JS calling all functions without waiting for the first one to complete is in the right direction. This is called "asynchronous requests". Please refer to How to return the response from an asynchronous call? for a detailed explanation.
The idea is to send your 3 requests and then wait for all of them to complete before checking the value of your squadron_counter variable (or whatever data you have updated in your success callbacks).
Then if my understanding is correct, you do not know how to implement this waiting?
Since you are using jQuery, the implementation is super simple. Note first that your jQuery.ajax request returns a Deferred object. So simply keep a reference of the Deferred object created by each AJAX request you send. Then you could use for example jQuery.when with a callback in its then method to wait for all your requests to complete:
function addSquadronByID(id) {
return jQuery.ajax({ /* ... */ }); // returns a Deferred object.
}
var d1 = addSquadronByID(3),
d2 = addSquadronByID(5),
d3 = addSquadronByID(6);
jQuery.when(d1, d2, d3).then(
// callback executed on success of all passed Deferred objects.
function () {
console.log(squadron_counter);
}
);
Demo: http://jsfiddle.net/btjq7wuf/
Related
I am writing some javascript that includes a series AJAX calls and I am looking for an elegant solution to the following issue: The goal of the script is to gather parameters and then execute an API call with these parameters. The very first time the call is executed there is one parameter that needs to be requested from the server - every subsequent call will use a stored value of this parameter. This is where the issue begins. I want a conditional AJAX call to be made only if this is the first time. I don't want to put the rest of the code into the success function of that AJAX call as that seems convoluted. I would like something like the following but due to the obvious asynchronous nature of the call I realize this is not possible. I also want to avoid having a synchronous call as this would cause the thread to block:
var myParameter;
if(!params.myParam.isStored) {
myParameter = getParamWithAjaxCall();
} else {
myParameter = params.myParam;
}
// Continue with the rest of execution here of which there is a lot of code
Sorry if this seems like an obvious question and I have looked into solutions using the following but I am looking for an experienced opinion on what the most elegant solution would be:
jQuery: when.done
jQuery: async: false
Passing a callback to the Ajax call
I would create a wrapper function which you pass your logic to as a callback in done(). Something like this:
function makeRequest(callback) {
if (!params.myParam) {
// retrieve param
$.ajax({
url: '/getParam',
success: function(data) {
params.myParam = data.param;
}
}).done(callback);
}
else {
// param already has a value...
callback();
}
}
makeRequest(function() {
// make your AJAX request here, knowing that params.myParam will have a value.
});
You could use promises like so (I have used JQuery promises here):
function ParameterValueProvider() {
var parameterValue;
return function() {
var deferred = $.Deferred();
if ( parameterValue === undefined ) {
$.ajax({
// ... ajax parameters go here
}).done(function(rsp) {
parameterValue = rsp;
deferred.resolve(parameterValue);
});
}
deferred.resolve(parameterValue);
return deferred;
}
}
// Your Application
(function() {
'use strict';
var getParam = ParameterValueProvider();
// this will get the value from server the firs time
// and subsequent calls will use the cached value
getParam().then(function() {
// subsequent ajax calls go here
});
}());
I have to make a bunch of JSONP calls, and execute some code after they all finish (or fail).
i.e.:
function finished() {
alert('done!');
}
function method1() {
return $.ajax('http://example.org/endpoint', {
data: {
foo: 'bar'
},
dataType: 'jsonp'
});
}
function method2() {
return $.ajax('http://example.org/endpoint', {
data: {
baz: 'qux'
},
dataType: 'jsonp'
});
}
$.when(method1(), method2()).always(finished);
The only problem is, if any of the requests fail, finished() will not be called.
I can try and detect if one of the ajax calls fails like so:
function method1() {
var method1_timeout = setTimeout(function() {
// this call has failed - do something!
}, 5000);
var ajax = $.ajax('http://example.org/endpoint', {
data: {
foo: 'bar'
},
dataType: 'jsonp',
success: function() {
clearTimeout(method1_timeout);
}
});
return ajax;
}
But I'm stuck at that point - how do I tell the deferred object that that particular ajax request has failed?
I tried calling the ajax object's error() method:
var method1_timeout = setTimeout(function() {
ajax.error();
}, 5000);
But no luck.
Any ideas?
I tested your posted code 'as-is' in jsFiddle and it works, even when the JSONP calls fails (of course, requests returns 404 errors because of example.com/endpoint queries).
So I think I found the answer of your problem in the jQuery documentation, in the $.ajax chapter:
The server should return valid JavaScript that passes the JSON
response into the callback function. $.ajax() will execute the
returned JavaScript, calling the JSONP callback function, before
passing the JSON object contained in the response to the $.ajax()
success handler.
Check if your JSONP returned JS code are valid and check if you don't have any syntax error in your console that will stop the execution of your JS code on your side (and prevent the always function to be executed).
EDIT: I reproduced your error. When using jQuery 2.x (edge), it works like intended (try there). Passing to jQuery 1.x (edge), the two methods calls are working but the always func is never called (try there). All my tests have been done under Google Chrome.
setTimeouts are in the global scope in JS and you created the variable ajax inside your function. Try
var method1_timeout = function(){ ajax.error();}
inside the function in which you defined var ajax and then provide method1_timeout to your setTimeout as a callback.
setTimeout(method1_timeout, 5000);
I'm trying to create a set of function (like App object below) that accepts preInit, Init, Load functions and executes them in order.
For example:
var formOperation = App.preInit(function(){
//Some Code ...
})
.init(function(){
//Some Code Here...
})
.load(function(){
//Other Code Here...
});
The problem is that I don't want the users deal with Deferred objects at all and I want them to use as many as ajax calls as they want. I don't want them to use functions that are extended to jQuery like ajaxQueue.
The only way I could think of is that to get the list of $.ajax calls and set Deferred object in them before calling them.
Do you have any idea how I can get the list of ajax calls in a function before calling the function?
Or do you have any other suggestions?
Well I think I've found a solution on this. $.ajaxSetup() has something called beforeSend which has the deferred object passed to it! I can create an array and add the deferred object to it.
var ajaxCalls = [{}];
$.ajaxSetup($.ajaxSettings, {
beforeSend: function (deferred) {
ajaxCalls.push(deferred);
},
complete: function (xhr, textStatus) {
if (ajaxCalls.length == 1) {
//Do Something...
}
}
});
I have an asynchronous Ajax function which runs a command string at the server side and returns the result to the client. It calls a callback to process the result.
function ajaxCall(commandStr,callback){
var url=......//make a url with the command string
jquery.get(url,function(result){
//process the result using callback
callback(result);
});
}
The asynchronous call (ajaxCall) may take a while to be finished but I want it to do the same command after an interval (1000ms).
I want to write a function that is like this:
function ajaxCallRepeated(interval,commandStr,callback)
I tried closures like this:
function ajaxCallRepeated(interval,commandStr,callback){
//This feature uses closures in Javascript. Please read this to know why and how: http://jibbering.com/faq/notes/closures/#clSto
function callLater(param1,param2,param3){
return (function(){
ajaxCall(param2,function(out,err){
if(param3)param3(out,err);
var functRef = callLater(param1,param2,param3);
setTimeout(functRef, interval);
});
});
}
//the first call
var functRef = callLater(interval,commandStr,callback);
setTimeout(functRef, interval);
}
Then I call it like this:
ajaxCallRepeated(2000,"ls",function(result){
alert(result);
});
But it only runs the command 2 times.
How can I write a function that will reschedule itself after it is called as a callback of an asynchronous function?
PS. I want to fire another Ajax call after the previous one is finished. Also, it worth to mention that axashCallRepeated() will be called with various parameters, so several Ajax calls are running in parallel, but for each commandStr, there is only one Ajax call going on, and after the Ajax call returns, another one will be fired after X seconds.
I would not use setTimeout to trigger the second Ajax call ! Because you never know how long it will take and if it's finished !
As far as you tagged your question right and you ARE using jquery you should consider something like this:
$.ajax({
type: 'POST',
url: url,
data: data,
success: function(){
// The AJAX is successfully done, now you trigger your custom event:
$(document).trigger('myAjaxHasCompleted');
},
dataType: dataType
});
$(function(){
//somehwere in your document ready block
$(document).on("myAjaxHasCompleted",function(){
$.ajax({
//execute the second one
});
});
});
So this would ensure that the ajax post is DONE and was successful and now you could execute the second one. I know its not the exact answer to your question but you should consider on using something like this ! Would make it safer I guess :-)
The key to solve this problem is to save a reference to the closure itself and use it when scheduling the next call:
function ajaxCallRepeated(interval,commandStr,callback){
//This feature uses closures in Javascript. Please read this to know why and how: http://jibbering.com/faq/notes/closures/#clSto
function callLater(_interval,_commandString,_callback){
var closure=(function(){
ajaxCall(_commandString,function(out,err){
if(_callback)_callback(out,err);
setTimeout(closure,_interval);
});
});
return closure;
}
//now make a closure for every call to this function
var functRef = callLater(interval,commandString,callback);
//the first call
functRef();
}
It becomes easier to reason about if you separate things up a bit.
For example, the repetition logic doesn't have to know about AJAX or callbacks at all:
function mkRepeater(interval, fn, fnScope, fnArgs) {
var running;
function repeat() {
if (!running) return;
fn.apply(fnScope, fnArgs);
setTimeout(repeat, interval);
}
return {
start: function() { running = true; repeat(); },
stop: function() { running = false; }
};
}
You can use it like this:
var r = mkRepeater(2000, ajaxFunction, this, ["getStuff", callbackFn]);
r.start();
...
r.stop();
If I use a closure to define something is there a means of waiting so to speak until the variable is populated before moving on to the next bit.
Example:
var myVari = someFunction();
$.each(myVari, function(){/*code for each*/});
the function that defines myVari is an AJAX call, which can take a second or 4 (yea its not to fast) to define the variable. Problem is, before the AJAX call yields its results the $.each has already fired off and errored due to myVari being empty. Is there a better way to approach this scenario?
You should adapt your code so that you can pass a callback to someFunction, which you execute when the AJAX call is completed.
The only way you can wait for the AJAX call to complete is to change the call to synchronous, but this is heavily discouraged as it locks up the browser completely for the duration of the AJAX call.
Because you are already using the jQuery libary, this process of callbacks becomes a whole lot easier. Instead of returning the variable like you are at the moment, I'd return the jQuery AJAX object (which has a promise interface as of 1.6), so you can easily add callbacks to it:
function someFunction () {
return jQuery.ajax('some/url.php', {
// whatever
});
}
var myVari = someFunction();
myVari.done(function (data) {
$.each(data, function(){/*code for each*/});
});
If I understand what you are trying to do, then you could try your $.each inside the 'success' handler of your ajax call.
Rewrite someFunction to something like -
var myVari; //define this here or in whichever calling scope where it needs to be available.
$.ajax({
'url': 'http://..',
'type': 'GET', // or POST
'data': { } // whatever data you need to send
'success': function(data) {
myVari = process_the_server(data);
$.each(myVari, function() {...});
}
});
Use a callback, like this:
someFunction(function(myVari) {
$.each(myVari, function(){ /*code for each*/ });
});
Then redefine someFunction like this:
function someFunction(callback) {
var myVari;
/* ... */
/* calcuate myVari */
/* ... */
/* instead of returning it, pass it to the callback: */
callback(myVari);
}
The correct way is: Instead of running the each on its own, run it inside the ajax call.
You could, I suppose do:
function checkFunc() {
setTimeout(function() {
if(myVari) {
$.each(........);
} else {
checkFunc();
}
}, 1000);
}
That not really good coding practice, but it will work.