Ajax call with nested ajax calls in for each - javascript

I'm experiencing the following problem.
I have the following nested / foreach loops ajax call structure:
var children = [];
$.fn.ajaxHelper.loadAjax({
url: someUrlReturningJsonOfChildren,
cache: true,
callback: function(options) {
var json = options.json;
$.each(json, function (i) {
// get the details of this child
$.fn.ajaxHelper.loadAjax({
url: urlForDetailsUsingId,
cache: true,
callback: function(options) {
var json = options.json;
children[i] = json;
}
});
}
}
});
// want to do something with (newly filled) children here
As you can imagine, I'm running into the trouble that the ajax calls are asynchronous (duh), but I want to do something with the children array only when all the ajax calls are done. Otherwise I'm obviously dealing with an incomplete array.
I have been looking at some jQuery solutions such as Deferred objects (using $.when().then() and such), but that would only solve the problem if I would not have the foreach-loop (as far as I can tell).
Also, changing the REST API (where the ajax calls are going) is not an option unfortunately, due to specified requirements regarding the API.
Ok, so without further ado: can anyone of you geniuses help me with this? :-)

ajax is asynchronous by default but you can turn it off. Here goes the API on how to do it
https://api.jquery.com/jQuery.ajax/
heres a little demp
$.ajax({
url: my_url.php,
type: "POST",
async: false,
dataType: 'json'
});
Or just make your next ajax call in a success function (Recommended)
function testAjax(handleData) {
$.ajax({
url:"getvalue.php",
success:function(data) {
//next ajax call here
}
});
}

You must run ajax query when previous query is completed with success (in jQuery onSuccess callback)

I had a smiler issue... below is a simplified version of my solution.
Step one: Declare global variables.
var results1,
results2,
[resultsN];
Step two: Make a function that accepts the results of each AJAX call as parameters.
function foo(results1, results2, [resultsN]) {
if (results1, results2, [resultsN]) {
//... do whatever you want with all of your results
}
}
Step three: Call all of the AJAX functions, set results to global variables, and call function foo for each.
function ajax() {
//AJAX call 1
$.ajax({
type: 'POST',
url: //URL,
success: function (data, textStatus, jqXHR) {
results1 = data;
},
dataType: 'json',
complete: function () {
foo(results1, results2);
}
});
//AJAX call 2
$.ajax({
type: 'POST',
url: //URL,
success: function (data, textStatus, jqXHR) {
results2 = data;
},
dataType: 'json',
complete: function () {
foo(results1, results2);
}
});
};
This method has the advantage of running as fast as the longest AJAX call takes. If you simply nest AJAX queries in the complete event then you will have to wait for each AJAX call to complete before moving to the next one...

Related

generalizing ajax call into function

I'm trying to attempt to generalize my ajax calls into a function as follows. I have not done this before and am not sure sure if I'm doing it correctly.
<script>
$(document).ready(function(){
var reg_no=$("#reg_no").val();
reg_no=reg_no.trim();
if(reg_no!==""){
//populate fields
data={reg_no:reg_no,func:"getSupplierDetails"};
success_function="updateFormFields";
ajax_call(data,success_function);
}
});
function ajax_call(data,success_function){
$.ajax({
type:"POST",
url:"../control/supplier-c.php",
dataType:"json",
data:data,
success:function(data){
success_function(data); //variable function works??
}
});
}
function updateFormFields(data){
//some code here to handle data array
}
</script>
What I'm trying to do here is avoid rewriting the whole ajax code by passing the data array and the function to be executed on success. What I'm not sure is the use of variable functions as i have done.
A note to be made is that the whole thing works for an ajax call if updateFormFields() code was moved into the success handler in the ajax call and the ajax_call() was not defined as a seperate function but implemented right after the comment "populate fields". I just have no experience in trying it this way and I need to know if this is possible or not.
Thank You
In Javascript, functions are first class objects, meaning you can pass them around as parameters.
function json_post(url, data, success_function, error_function) {
$.ajax({
type:"POST",
url:url,
dataType:"json",
data:data
}).then(success_function, error_function);
}
Then you can call it as
json_post("../control/supplier-c.php", { data: "data" }, function (res) {
console.log('Ajax req successful');
console.log(res);
}, function (res) {
console.log('Error in ajax req');
console.log(res);
});
In your case, you can do:
ajax_call(data, updateFormFields);
and it should work as expected.
There's no need to wrap the success function, you can just apply apply it directly.
function ajax_call(data, success_function) {
$.ajax({
...
success: success_function
});
}
An even better idea is to avoid the legacy success and error callbacks and instead return the jQuery promise. You can use standard promise methods .then() and `.
function ajax_call(data) {
return $.ajax({
...
});
}
ajax_call()
.then(function(data) {
// this runs if it succeeds
})
.fail(function(err) {
// this runs if it failed
});
Promises have a huge benefit to being chain-able, making the code flatter, avoiding the nest of "christmas tree callbacks".
I would recommend checking success_function as well as failure_function to handle server response (XHR) errors also.
function success_function(){
//code to handle success callback
}
function error_function(){
//code to handle failure callback
}
function ajax_call(data, success_function, error_function) {
if (typeof success_function === 'function' && typeof error_function === 'function') {
$.ajax({
type: "POST",
url: "../control/supplier-c.php",
dataType: "json",
data: data,
}).then(success_function).fail(error_function);
}
}

Jquery Ajax Call Return

I have made a function which is the one below that i pass data to and returns the result as is. I made this way because i will be needing a lot of ajax call and i just made a function that i pass the data to and get the result as is and work with the result.
function FunctionsCall(data){
var ret;
$.ajax({
type:'GET',
url: 'includes/helpers/functions.php',
dataType:"json",
data: data,
success: function(result){
ret = result;
}});
return ret;}
Now i am calling it where i need it:
$('#register-name, #register-surname').keyup(function(e) {
var x = FunctionsCall({query: $(this).val(), funcid: 1});
(x!==1) ? $(this).addClass('input-has-error') : $(this).removeClass('input-has-error'); });
But strange is that i always see x as undefined. Pointing out the ret is filled with either 1 or 0 i don't know why it is not being passed to x.
Can you please help me out? It might be simple but i just experiment when needed with javascript and jquery.
Regards
ret doesn't get set until the success function runs, which is when the ajax finishes. FunctionCall returns straight away however. You'll either need to return the ajax deferred object or put your addClass/removeClass functionality in your success function.
A way to add your addClass/removeClass functionality to your success function would be like this:
function FunctionsCall(data, successFn) {
$.ajax({
type: 'GET',
url: 'includes/helpers/functions.php',
dataType: "json",
data: data,
success: successFn
});
}
$('#register-name, #register-surname').keyup(function(e) {
var element = $(this);
var data = { query: element.val(), funcid: 1 };
var successFn = function(x) {
if (x !== 1) {
element.addClass('input-has-error')
} else {
element.removeClass('input-has-error');
}
}
FunctionsCall(data, successFn);
});
The problem is that the ajax call takes time to execute, whereas your processing of x is immediately after the call to FunctionsCall
Imagine that in order to go to the php file and get the result, the browser has to send a request over the wire, the server needs to process the request and return the value, again over the wire. This process takes an unpredictable amount of time as it relies on network connections and server specs / current load.
The code to call the function and process the result happens immediately after this step and as such won't have the required values when it is run (browsers are much quicker at executing the next step than networks are at processing requests).
The best thing to do is to wrap your processing code up in it's own function, so it isn't immediately called, then call that function with the result once you get it. Like this:
// function defined, won't be called until you say so
var processMe = function(result) {
alert(result);
}
$.ajax({
// ajax params
success: function(result) {
// function called within success - when we know the request is fully
// processed, however long it takes
processMe(result));
}
});
You could also do the processing directly in the success block but the advantage of using a function is it's there to re-use in the future, plus, you also get to give it a nice understandable name, like outputValidatedMessage.
you must send ajax request syncronous
function FunctionsCall(data){
var ret;
$.ajax({
type:'GET',
async: false,
url: 'includes/helpers/functions.php',
dataType:"json",
data: data,
success: function(result){
ret = result;
}
});
return ret;
}
Ajax calls are asynchronous.
This means that while you call $.ajax(), the function continues to run and return x which is undefined, as the ajax response has not been send yet.
function FunctionsCall(data){
var ret;
$.ajax({
type:'GET',
async: false,
url: 'includes/helpers/functions.php',
dataType:"json",
data: data,
success: function(result){
ret = result;
}
});
return ret;
}
The below should work for you
function FunctionsCall(data){
var ret;
$.ajax({
type:'GET',
url: 'includes/helpers/functions.php',
dataType:"json",
data: data,
success: function(result){
(result !==1 ) ? $(this).addClass('input-has-error') : $(this).removeClass('input-has-error'); });
}});
}
maybe is because the ajax function is called asynchronously so the line var x= .... doesn't wait for the asignment and thats why is undefined. for that you should use a promise here is an example http://joseoncode.com/2011/09/26/a-walkthrough-jquery-deferred-and-promise/
http://www.htmlgoodies.com/beyond/javascript/making-promises-with-jquery-deferred.html
check if the following works, may be your GET method is taking time to execute.
var x;
function FunctionsCall(data){
var ret;
$.ajax({
type:'GET',
url: 'includes/helpers/functions.php',
dataType:"json",
data: data,
success: function(result){
ret = result;
x= result;
alert(x)
}});
return ret;}
if the snippet works, you should make you synchronous async: false or make callback function
try this code.
function FunctionsCall(data,callback) {
try {
ajax({
type: 'GET',
url: 'includes/helpers/functions.php',
dataType: "json",
data: data,
success: function (result) {
callback(result);
}
});
} catch(e) {
alert(e.description);
}
}
$('#register-name, #register-surname').keyup(function (e) {
var data = {
uery: $(this).val(),
funcid: 1
};
FunctionsCall(JSON.stringify(data), function (result) {
(result !== 1) ? $(this).addClass('input-has-error') : $(this).removeClass('input-has-error');
});
});

Multiple looped ajax requests with one callback

Like in this article, I have several ajax requests to perform followed by 1 action.
However, the difference is that all my ajax request only differ by one incremental parameter like this:
$.when(
// ajax requests
// 1
$.ajax({
url:"https://www.aaaaaaa.com?param="+0,
crossDomain: true,
dataType: "jsonp",
success: function (response) {
data = data.concat(response);
}
}),
// 2
$.ajax({
url:"https://www.aaaaaaa.com?param="+2500,
crossDomain: true,
dataType: "jsonp",
success: function (response) {
data = data.concat(response);
}
}),
// 3
$.ajax({
url:"https://www.aaaaaaa.com?param="+5000,
crossDomain: true,
dataType: "jsonp",
success: function (response) {
data = data.concat(response);
}
})
// etc. ~10 times
).then(function() {
// action
console.log(data);
});
Like python I don't like to repeat myself 10 times.
I tried to make a for loop but it seems not possible to write for loops in the $.when().
Any ideas how to achieve this ?
I searched everywhere whithout results.
Many thanks,
What should probably work is to define a function before your $.when something like this:
function createRequest(port) {
return $.ajax({
url:"https://www.aaaaaaa.com?param="+port,
crossDomain: true,
dataType: "jsonp",
success: function (response) {
data = data.concat(response);
}
})
}
and then use that in your $.when
$.when(createRequest(0), createRequest(2500), createRequest(5000));
And if you want to create this function call dynamically with more parameters you can create an array of these requests for-loop and then call $.when.apply(this, array)
$.when.apply(this, your_request_array)
See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
Hope this helps
You can use Kriskowal's q implementation : https://github.com/kriskowal/q
there is a method Q.allSettled(arrayOfPromises) that suits your needs.
eg :
Q.allSettled(promises)
.then(function (results) {
results.forEach(function (result) {
if (result.state === "fulfilled") {
var value = result.value;
} else {
var reason = result.reason;
}
});
});
Angular has based his $q directive on this
Just put your promises in an array:
var promises = [0, 2500, 5000].map(function(n) {
return $.ajax(...); // appending `n` to the URL as required
});
and then call $.when.apply:
$.when.apply($, promises).then(...)
The parameters passed to the .then callback will be individual arrays, each containing the three parameters that a single $.ajax callback receives.
BTW, your current code will concat the arrays in whatever order the calls complete, not necessarily in the order they were started.
If the order of concatenation matters, you should use those .then function parameters rather than your existing success handlers to create your data variable:
then(function() {
var data = [];
[].forEach.apply(arguments, function(response) {
data = data.concat(response[0]);
});
});

Return result from nested asynchronous ajax call

I need to return the data from an nested ajax call but also need the ajax call to stay asynchronous.
I found some related questions but can't get it to work in the situation below. I assume returnData is being returned before the ajax call is done, but I can't find an solution.
function makeRequest(command, postData){
var returnData;
$.ajax({
url: 'call.php?command='+command,
data: postData,
type: 'POST'
}).done(function(data){
returnData = data;
});
return returnData;
}
Yes since this call is async returnData is returned immediately. If you need to use returndata pass it to a function in the callback
function makeRequest(command, postData, functionToCallAfterAjax){
var returnData;
$.ajax({
url: 'call.php?command='+command,
data: postData,
type: 'POST'
}).done(function(data){
functionToCallAfterAjax(data);
});
}
Of course you could pass the function to call as a parameter.
This means that if your code was meant to do:
var returnedData = makeRequest(command, postData);
anotherFunction(returnedData);
you should do simply (using the code above)
makeRequest(command, postData, anotherFunction);
and everything will work
You can't. Asynchronous events don't work like that.
The HTTP request is sent, then the function that triggers it continues immediately. The callback won't be fired until the HTTP response has arrived, but which time the calling function will have ended.
You must have your callback (the function you are passing to done()) perform whatever further processing is needed.
You're returning the value of returnData before the .done() function has run. Correct your code by passing the received data to a function for processing:
function makeRequest(command, postData){
$.ajax({
url: 'call.php?command='+command,
data: postData,
type: 'POST'
}).done(function(data){
HandleCallBack(data);
});
}

waiting for an ajax reply

I have a javascript function called GetRequest() that calls the server with $.ajax() and receives a json string:
function GetRequest(ThePage) {
RequestedPage = parseInt(ThePageNumber,10);
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "../Pages/MyPage.aspx/GetPage",
data: "{'ThePage':'" + ThePageNumber + "'}",
dataType: "json",
success: function (msg) {
var data = msg.hasOwnProperty("d") ? msg.d : msg;
OnSucessCallBack(data);
},
error: function (xhr, status, error) {
alert(xhr.statusText);
}
});
};
I have a function called ShowData() that calls GetRequest() and must wait until GetRequest receives its data before continuing.
function ShowData() {
//some code that determines the page number
GetRequest(ThePageNumber);
//wait until the data is in
};
I use GetRequest in several places so I can't use its success function in the context of ShowData.
How do I make my function ShowData pause its execution until GetRequest is finished? I thought of changing the OnSuccessCallBack function and determine which function initially called GetRequest() but I'm not sure on how to best do this.
Thanks for your suggestions.
add a function pass-in to GetRequest like so:
function GetRequest(pageNumber, successCallback){
//I admit this isn't "elegant" but it's most assuredly readable
var callback;
if (successCallback == null) {
callback = //default callback definition here
} else {
callback = successCallback;
}
//do everything else the same here
//except use the callback defined above
}
This gives you the flexibility to add in a separate callback handler for the onsuccess
Alternately, do the same as above, but use the "onComplete" handler unless you need the data specifically on the return (it doesn't appear as tho you do).
I'm going to strenuously suggest that you do use callbacks for asynchronous code instead of trying to shoehorn in sync requests. It's much better to just adopt a coding style that revolves around async requests when working in javascript, especially where you're already doing AJAX (which is by definition intended to be async).
Pass async:false along with the ajax options..
$.ajax({
type: "POST",
async:false,
.
.
.
});
You can make your call only synchronous by using ajax prefilters:
function ShowData() {
//some code that determines the page number
$.ajaxPrefilter( function( options, originalOptions, jqXHR ) {
options.async = false;
});
GetRequest(ThePageNumber);
//wait until the data is in
};

Categories

Resources