jQuery Multiple getJSON requests - javascript

My script needs to fetch several json files on https://graph.facebook.com/xxxx, and retrieve a certain field from each json, then calculate summation.
My problem is how to print out the result after all getJSON done? With below code it will prints 0. Feel free to suggest any better approaches.
var result = 0;
$.each(urls, function (i, url) {
$.getJSON(url, function (json) {
result += json.field1;
})
});
alert(result);

Using jQuery 1.5 deferred objects:
Accumulate an array of the JQXHR objects returned by $.getJSON()
var jxhr = urls.map(function(url) {
return $.getJSON(url, function(json) {
result += json.field1;
})
});
and only $.when they're all .done():
$.when.apply($, jxhr).done(function() {
alert(result);
});
NB: this will accumulate result in the order that the AJAX calls complete, not in the order they're made.

It's not working as you are printing the result straight away, remember that the code where you concatenate the result is a callback so will fire after your alert.
On each callback you'll have to check if all have finished. replace the alert() call with your processing. :)
var result = 0;
var doneCount = 0;
$.each(urls, function (i, url) {
$.getJSON(url, function (json) {
doneCount++;
result += json.field1;
if (doneCount == urls.length) {
alert(result);
}
})
});

Related

Ajax asynchronicity calls end function before inner loop function

This is the code i am struggling with:
$(document).ready(function () {
var dataSets = [];
getSensorIDs().done(function (result) {
var sensorIDs = jQuery.parseJSON(result);
$.each(sensorIDs, function (index, value) { //loop through each id
getDataBySensorID(value.id).done(function (res) {
var temperatureData = jQuery.parseJSON(res);
var dataPoints = [];
$.each(temperatureData, function (index, value) {
dataPoints.push({
x: index
, y: value.value
});
})
dataSets.push(buildDataset(dataPoints, 1));
alert("Pushed something.");
});
});
alert("Drawed something.");
drawChart(dataSets);
})
});
The problem is: Drawed something happens ahead of Pushed something even though the program flow doesn't like look it would. I thought i fixed the problem of ajax (it's asynchron) with .done() but it doesn't seem like that. What would be a proper fix for this problem?
It is working exactly as it should because the "pushed something" code is in a NESTED AJAX request's done callback. That will be sometime in the future. And so, the JavaScript engine continues on processing the rest of the code, which includes "Drawed something" in the top-level AJAX request next, so that happens.
At some point in the future, the AJAX call will complete and the done callback will fire and then you will get "pushed something".
// 1. When the document is ready...
$(document).ready(function () {
var dataSets = [];
// 2. Make the AJAX call, BUT DONE RUN .done YET!
getSensorIDs().done(function (result) {
var sensorIDs = jQuery.parseJSON(result);
// loop through each id
$.each(sensorIDs, function (index, value) {
// 3. As part of the first AJAX call, make a SECOND one
getDataBySensorID(value.id).done(function (res) {
var temperatureData = jQuery.parseJSON(res);
var dataPoints = [];
$.each(temperatureData, function (index, value) {
dataPoints.push({
x: index
, y: value.value
});
});
// 5. The nested call finishes last
dataSets.push(buildDataset(dataPoints, 1));
alert("Pushed something.");
});
});
// 4. This runs first because the first AJAX call is finishing
// before the nested one (above)
alert("Drawed something.");
drawChart(dataSets);
});
});

Need to make multiple Async calls before executing next step JS

I have an array that can hold an unknown amount of indexes in it. Each index is used to send data with an ajax call. I am looping through with a for loop gathering the data from the successful call and pushing it into an empty array. At the end of the unknown amount of calls I then need to use that newly gathered array in my view. newDataArray is executed at the bottom before the loops are done and therefor it is still empty. How do I finish all the calls then do what is at the bottom?
If it helps, I am doing this in React with the Flux pattern. But the same issue may be done not in React. Here is a mock sample of what I am trying to do:
JS
case 'execute-calls':
//This is the new array to push to
var newDataArray = [];
//Url to call
var url = 'http://dev.markitondemand.com/Api/v2/Quote/jsonp';
for(let i = 0; i < payload.data.length; i++){
//given array of data that needs to be sent with call
let symb = { symbol: payload.data[i]};
$.ajax({
data: symb,
url: url,
dataType: "jsonp",
})
.done(function(data){
let updatedData = {
//...data that is stored from response
};
newDataArray.push(updatedData);
})
.fail(function(error){
//console.log(error);
});
}
//This will be updating the state object which is above the switch cases
//However this is ran before the end of the loops so newDataArray is empty
var updateTicker = {
updatedTicker: true,
updatedTickerSymbols: newDataArray
};
assign(stockData,updateTicker);
getStockData.emitChange();
break;
You can make use of the fact that $.ajax() actually returns a deferred object, and use it to create an array of deferreds. e.g.
var symbols = [1, 2, 3, 4];
var deferreds = symbols.map(function (symbol) {
return $.ajax({
url: 'http://dev.markitondemand.com/MODApis/Api/v2/Quote/jsonp',
data: { symbol: symbol },
dataType: 'jsonp'
});
});
You can resolve multiple deferreds at once with $.when(). There is a complication however, $.when() expects a list of parameters rather than array. We can solve this by using Function#apply.
To add to the complication, the callback function is also called with a list of arguments. Since we don't know how many arguments there are, we'll use the arguments pseudo-array. And since arguments isn't an actual array, we'll loop through it by using Function#call on Array#prototype.
$.when.apply($, deferreds).done(function () {
Array.prototype.forEach.call(arguments, function (response) {
console.log(response[0].Message);
});
}).fail(function (jqXHR, textStatus, error) {
console.error(error);
});
[UPDATED to include fail() call]
If you're using ES6 this is much more elegant:
$.when(...deferreds).done((...responses) => {
responses.forEach((response) => {
console.log(response[0].Message);
});
});
When ever you are dealing with ajax calls and have to do some operations at the end of all async calls then better choice would be to use Callback functions.
Modifying your code to use the call back,
function AsyncLoopHandler(index) {
if (index > payload.data.length) {
// all the indexes have finished ajax calls do your next step here
var updateTicker = {
updatedTicker: true,
updatedTickerSymbols: newDataArray
};
assign(stockData, updateTicker);
getStockData.emitChange();
}
else {
//given array of data that needs to be sent with call
let symb = { symbol: payload.data[index] };
$.ajax({
data: symb,
url: url,
dataType: "jsonp",
})
.done(function (data) {
let updatedData = {
//...data that is stored from response
};
newDataArray.push(updatedData);
AsyncLoopHandler(index++); // call the function again with new index
})
.fail(function (error) {
//console.log(error);
});
}
}
Now for starting this recursive function just start it by passing the index 0.
AsyncLoopHandler(0);
So all the ajax calls will be executed one after the other as if its an synchronous requests, And the if check will see if all the indexes are complete and then run your logic. Let me know if this helps
suggest use promise, logic would like
var urls= [x,x,x,x];
var results = [];
var qs = $.map(urls,function(url){
return function(){
var deferred = Q.defer();
$.ajax({
success:function(){
results.push(url)
deferred.reslove();
},error:function(){
deferred.reslove();
}
})
return deferred;
}
})
Q.all(qs).then(function(){
console.log(results )
});
or use yield and co in new standard
https://github.com/kriskowal/q

Dynamic multiple Deferred jQuery Ajax calls

Using the Deferred pattern from jQuery http://api.jquery.com/jQuery.when/, I am trying to make multiple jsonp ajax calls and wait for the results before moving to the next step. I can accomplish this using a fixed amount of calls because I can set the number of resolved argument parameters in the ".done()" deferred object. But in my application it doesn't work because the number of calls is dynamic and always unknown.
This first simplified example works because I can set the number of args in the .done() resolved function. I know I need two because there are two calls in the .when():
$.when( $.ajax( url1 ), $.ajax( url2 ) ).done(function( a1, a2 ) {
var data = a1[ 0 ] + a2[ 0 ];
});
This is what I need but can't get it to work:
var urls = GetUrlList(); // returns array of urls to json service
var requests = []; // hold ajax request
for (i = 0; i < urls.length; i++) {
requests.push($.ajax(url[i]));
}
$.when.apply($, requests).done(function ("what goes here?") {
// Need to get the data returned from all ajax calls here
});
Thanks for any help on this!
You can use arguments, which is a special king of object holding all arguments passed to a function
$.when.apply($, requests).done(function () {
console.log(arguments); //it is an array like object which can be looped
var total = 0;
$.each(arguments, function (i, data) {
console.log(data); //data is the value returned by each of the ajax requests
total += data[0]; //if the result of the ajax request is a int value then
});
console.log(total)
});

Any better way to combine multiple callbacks?

I need to call an async function (with loop) for multiple values and wait for those results. Right now I'm using the following code:
(function(){
var when_done = function(r){ alert("Completed. Sum of lengths is: [" + r + "]"); }; // call when ready
var datain = ['google','facebook','youtube','twitter']; // the data to be parsed
var response = {pending:0, fordone:false, data:0}; // control object, "data" holds summed response lengths
response.cb = function(){
// if there are pending requests, or the loop isn't ready yet do nothing
if(response.pending||!response.fordone) return;
// otherwise alert.
return when_done.call(null,response.data);
}
for(var i=0; i<datain; i++)(function(i){
response.pending++; // increment pending requests count
$.ajax({url:'http://www.'+datain[i]+'.com', complete:function(r){
response.data+= (r.responseText.length);
response.pending--; // decrement pending requests count
response.cb(); // call the callback
}});
}(i));
response.fordone = true; // mark the loop as done
response.cb(); // call the callback
}());
This isn't all very elegant but it does the job.
Is there any better way to do it? Perhaps a wrapper?
Async JS to the rescue (for both client-side and server-side JavaScript)! Your code may look like this (after including async.js):
var datain = ['google','facebook','youtube','twitter'];
var calls = [];
$.each(datain, function(i, el) {
calls.push( function(callback) {
$.ajax({
url : 'http://www.' + el +'.com',
error : function(e) {
callback(e);
},
success : function(r){
callback(null, r);
}
});
});
});
async.parallel(calls, function(err, result) {
/* This function will be called when all calls finish the job! */
/* err holds possible errors, while result is an array of all results */
});
By the way: async has some other really helpful functions.
By the way 2: note the use of $.each.
You can use the jQuery Deferred object for this purpose.
var def = $.when.apply(null, xhrs) with xhrs being an array containing the return values of your $.ajax() requests. Then you can register a callback def.done(function() { ... }); and use the arguments array-like object to access the responses of the various requests. to properly process them, remove your complete callback and add dataType: 'text' and use the following callback for done():
function() {
var response = Array.prototype.join.call(arguments, '');
// do something with response
}

getJSON wont allow array to pass through

function near_home(lat,long,miles) {
var url = "api_url";
var places = [];
$.getJSON(url, function(data) {
$.each(data['results'], function(i, place) {
places.push(place);
});
console.log(places);
});
console.log(places);
return places;
}
So the first console.log() will return the desired objects. But the second console method results in null data. I've rewritten this thing a few times over and can't seem to find reason for this madness. What am I missing?
AJAX requests are asynchronous. You need to execute all following code in a callback.
A possible solution would be this:
function near_home(lat,long,miles,cb) {
var url = "api_url";
var places = [];
$.getJSON(url, function(data) {
$.each(data.results, function(i, place) {
places.push(place);
});
cb(places);
});
}
When using the function, call it like this:
near_home(lat, long, miles, function(places) {
// your code using the returned array
});
The getJSON() method is asynchronous so places will not have been filled at the second console.log-statement.

Categories