Get response of multiple deferred objects in jquery - javascript

Below are my Multiple ajax calls with promises.
$(window).load(function(){
$.when(getApiModemList()).done(function(vendors){
var deferreds = calculateApiBalances(vendors);
$.when.apply($,deferreds).done(function(balance) {
console.log(balance);
console.log("All Done");
});
});
function getApiModemList(){
return $.getJSON("url");
}
function calculateApiBalances(vendors)
{
var defer=[];
$.each(vendors,function(k,v){
defer.push($.getJSON(someurl));
});
return defer;
}
});
Function calculateApiBalances() return me some balance which I need to Sum up to get total of all balance .
But when print console.log(balance) it doesnot provide me with valid array of balance json.
Another issue is if any one of my ajax calls in calculateApiBalances() fails it doesnot prints All Done.
What should be done in above code to achieve this.

But when print console.log(balance) it doesnot provide me with valid array of balance json.
That's a weird thing of $.when. It doesn't provide you with an array, but calls your callback with multiple arguments.
Another issue is if any one of my ajax calls in calculateApiBalances() fails it doesnot prints All Done.
Yes, because when one promise fails the whole $.when() promise is rejected immediately, and you don't have any error handler. You'll have to catch errors individually if you want to always get an array (of possibly invalid responses). See also $.Deferred: How to detect when every promise has been executed.
What should be done in above code to achieve this.
First of all, avoid the deferred antipattern :-)
$(window).load(function() {
getApiModemList().then(calculateApiBalances).then(function() {
var balance = Array.prototype.slice.call(arguments);
console.log(balance);
console.log("All Done");
});
});
function getApiModemList() {
return $.getJSON(somurl).then(function(res) {
return res.data;
});
}
function calculateApiBalances(vendors) {
return $.when.apply($, $.map(vendors, function(v, k) {
return $.getJSON(someurl).then(null, $.when);
}));
}
EDIT by Roamer:
And here's a version of the master routine with a mechanism for summing the balances.
getApiModemList().then(calculateApiBalances).then(function() {
var sumOfBalances = Array.prototype.reduce.call(arguments, function(tot, obj) {
return tot + (+obj.balance || 0);
}, 0);
console.log(sumOfBalances);
console.log("All Done");
});
obj is the object promised by $.getJSON(someurl) in calculateApiBalances(). In the case of a $.getJSON() error, obj will be a jqXHR object, obj.balance will be undefined and +obj.balance will be NaN, therefore default to adding zero; otherwise add obj.balance .
If you wanted to know how many of the getJSON requests were successful and how many were unsuccessful, then there's some more code to write, but not a lot.

Related

Wrapping $.ajax with Promise doesn't resolve

What I've been trying is to call $.ajax multiple times, until the result is empty.
Here is a simplified version of my experimentation...
var objects = [];
function getObjects (page){
return new Promise(function(resolve, reject){
$.ajax({
url : url+"&pageNumber="+page,
type:'get',
dataType:'json',
async : true,
}).done(function(results){
if(results.length > 0){
objects = objects.concat(results);
return getObjects(page + 1);
}
console.log("Finished",objects);
resolve(objects);
}).fail(reject);
});
};
var page = 0;
getObjects(page).then(function(objects) {
console.log("getObjects completed",objects);
}).catch(function(error){
console.log(error);
});
Put simply, getObjects(page) will be called repeatedly until all the objects are retrieved.
The first console.log() shows what I expect, but the then function at the end doesn't run.
Interestingly, when I intentionally make the function fail, the catch part works properly. Clearly, resolve isn't working, but I don't see why.
Any advice will be appreciated.
EDIT
I've tried reject and reject(objects), neither worked.
jQuery Ajax requests are promises. They may not be Promise instances, but they implement the Promise interface. There is no need to wrap them.
function getObjects(page, objects) {
page = page || 0;
objects = objects || [];
return $.get(url, {pageNumber: page}).then(function (results) {
objects.push.apply(objects, results);
return results.length ? getObjects(page + 1, objects) : objects;
});
}
getObjects().done(function (objects) {
console.log("getObjects completed",objects);
}).fail(function(error){
console.log(error);
});
Also, in this case there is no reason to use the more wordy $.ajax(). $.get() will do just fine. jQuery will automatically detect a JSON reponse, and Ajax requests are async anyway, so setting the dataType and async parameters is redundant.

How to make queries in loop using Parse promises in cloud code?

The response received is always an empty array. The inner stands_query in the for loop never gets executed. Also I would like to know if there is again an inner query inside the stands query then how do I achieve that.
STANDS CLASS STADIUMS CLASS Code below :
var final_list = [];
query.find().then(function(stadiums){
_.each(stadiums,function(stadium){
var stands_query = new Parse.Query(“Stands");
stands_query.equalTo(“stdId”,stadium.get(“stdId"));
var promise = stands_query.find().then(function(stands){
_.each(stands,function(stand){
var jsonObject = {
“stdId": stand.get(“stdId").id,
}
final_list.push(jsonObject);
});
return null;
},function(error){
return response.error(error);
});
return promise;
});
}).then(function(){
response.success(final_list);
});
Your first .then isn't returning anything. I'll break your code down so you can see it:
query.find().then(function(stadiums){ //anonymous function 1
_.each(stadiums,function(stadium){ //anonymous function 2
return "foo" //this returns "foo" as a result of anonymous function 2.
});
//Nothing explicitly returned from function 1!
}).then(function(){
response.success(final_list);
});
A function that lacks an explicit return statement will return undefined. Your code then executes "response.success" before any of the internal promises resolve.
What you could do instead is create an array of internal promises that you wait for with Parse.Promise.when:
query.find().then(function(stadiums){
var promises = [];
_.each(stadiums,function(stadium){
var promise = stands_query.find().then(...)
promises.push(promise);
});
//if returning another promise, the ".then" won't execute until it completes.
return Parse.Promise.when(promises);
}).then(function(){
response.success(final_list);
});
All this being said, you may run into timeout issues depending on how large your dataset is. Consider rewriting your query so that you query for Stands belonging to a Stadium with relational queries instead.
Update
Now that you've updated your question with fields, it looks like your line stands_query.equalTo(“stdId”,stadium.get(“stdId")); has two mistakes and will never return results. It should be stands_query.equalTo(“stadiumId”,stadium);.
We have many Stadiums, and every stadium has many Stands. The relationship between Stadium and Stand is represented in data by a pointer column on the Stands class called "stadiumId".
In comments, the functional goal is stated very simply: a JSON array of stands. This requires a single query, no looping at all:
function allTheStands() {
var query = new Parse.Query("Stands");
query.include("stadiumId");
return query.find().then(function(stands) {
return JSON.stringify(stands);
});
}
// call it like this:
allTheStands().then(function(jsonStands) {
// jsonStands is all of the stands represented as son
});
EDIT
A more roundabout way to the same result is to not include stadiumId in the query, instead doing a fetch after the stands query completes.
(This is just a specific form of advice given by #adamdport, given details of your data. You should credit his answer if you find this useful).
// adding underscorejs for handling arrays and other utils
var _ = require('underscore');
function allTheStands() {
var stands;
var query = new Parse.Query("Stands");
return query.find().then(function(result) {
stands = result;
// we're not done yet, because we need to fetch each stand's stadium
var promises = _.map(stands, function(stand) {
return stand.get("stadiumId").fetch().then(function(stadium) {
stand.set("stadiumId", stadium);
});
});
// as adamdport suggests, the crux of the looping answer is to use Promise.when()
return Parse.Promise.when(promises);
}).then(function() {
return JSON.stringify(stands);
});
}

How to wait for AsyncRequests for each element from an array using reduce and promises

I'm new on AngularJS and JavaScript.
I am getting remote information for each of the elements of an array (cars) and creating a new array (interested prospects). So I need to sync the requests. I need the responses of each request to be added in the new array in the same order of the cars.
I did it first in with a for:
for (a in cars) {
//async request
.then(function () {
//update the new array
});
}
This make all the requests but naturally didn't update the new array.
After seeking in forums, I found this great examples and explanations for returning a intermediate promise and sync all of them.
1. http://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
2. http://stackoverflow.com/questions/25605215/return-a-promise-from-inside-a-for-loop
3. http://www.html5rocks.com/en/tutorials/es6/promises/
(#MaurizioIndenmark, #Mark Rajcok , #Michelle Tilley, #Nolan Lawson)
I couldn't use the Promise.resolve() suggested in the second reference. So I had used $q.defer() and resolve(). I guess I have to inject a dependency or something else that I missed. As shown below:
In the Controller I have:
$scope.interestedProspects = [] ;
RequestDetailsOfAsync = function ($scope) {
var deferred = $q.defer();
var id = carLists.map(function (car) {
return car.id;
}).reduce(function (previousValue, currentValue) {
return previousValue.then(function () {
TheService.AsyncRequest(currentValue).then(function (rData) {
$scope.interestedProspects.push(rData);
});
});
}, deferred.resolve());
};
In the Service I have something like:
angular.module('app', []).factory('TheService', function ($http) {
return {
AsyncRequest = function (keyID) {
var deferred = $q.defer();
var promise = authorized.get("somep.provider.api/theService.json?" + keyID).done(function (data) {
deferred.resolve(data);
}).fail(function (err) {
deferred.reject(err);
});
return deferred.promise;
}
}
}
The displayed error I got: Uncaught TypeError: previousValue.then is not a function
I made a jsfiddle reusing others available, so that it could be easier to solve this http://jsfiddle.net/alisatest/pf31g36y/3/. How to wait for AsyncRequests for each element from an array using reduce and promises
I don't know if the mistakes are:
the place where the resolve is placed in the controller function.
the way the reduce function is used
The previousValue and currentValue sometimes are seen by javascript like type of Promise initially and then as a number. In the jsfiddle I have a working example of the use of the reduce and an http request for the example.
Look at this pattern for what you want to do:
cars.reduce(function(promise, car) {
return promise.then(function(){
return TheService.AsyncRequest(car).then(function (rData) {
$scope.details.push(rData);
});
});
}, $q.when());
This will do all the asynchronous calls for every car exactly in the sequence they are in the cars array. $q.all may also be sufficient if the order the async calls are made doesn't matter.
It seems you are calling reduce on an array of ids, but assume in the passed function that you are dealing with promises.
In general, when you want to sync a set of promises, you can use $q.all
You pass an array of promises and get another promise in return that will be resolved with an array of results.

Continue of failure of jQuery Deferred chain

I'm doing a series of sequential AJAX calls in jQuery, using the usual method of chaining with Deferred. The first call returns a list of values and the subsequent calls are made with those returned list entries. After the first call that returns the list, the subsequent calls may be done in any order, but they must be done one at a time. So this is what I use:
$.when(callWebService()).then(
function (data) {
var looper = $.Deferred().resolve(),
myList = JSON.parse(data);
for (var i in myList) {
(function (i) {
looper = looper.then(function () { // Success
return callWebService();
},
function (jqXHR, textStatus, errorThrown) { // Failure
if (checkIfContinuable(errorThrown) == true)
continueChain();
else
failWithTerribleError();
});
})(i);
}
});
It turns out that the subsequent calls may fail at times, but I still want to do the remainder of the calls. In my listing, that's what this little bit of inventive pseudo code is meant to do:
if (checkIfContinuable(errorThrown) == true)
continueChain();
else
failWithTerribleError();
How on earth do I implement continueChain though? It appears as though a failure on any deferred will cause the rest of the chain to also fail. Instead, I'd like to log the error and continue with the rest of the list.
With Promises/A+ this is as easy as
promise.then(…, function(err) {
if (checkIfContinuable(err))
return valueToConinueWith;
else
throw new TerribleError(err);
})
Unfortunately, jQuery is still not Promises/A+ compliant, and forwards the old value (result or error) - unless you return a jQuery Deferred from the callback. This works just the same way as rejecting from the success handler:
jDeferred.then(…, function(err) {
if (checkIfContinuable(err))
return $.Deferred().resolve(valueToConinueWith);
else
return $.Deferred().reject(new TerribleError(err));
})
Recovering from an error in a jQuery promise chain is more verbose than with Promises/A+ implementations, which naturally catch errors in a .catch's or a .then's error handler. You have to throw/rethrow in order to propagate the error state.
jQuery works the other way round. A .then's error handler (.catch doesn't exist) will naturally propagate the error state. To emulate "catch", you have to return a resolved promise, and the chain will progress down its success path.
Starting with an array of items on which you want to base a series of async calls, it's convenient to use Array.prototype.reduce().
function getWebServiceResults() {
return callWebService().then(function(data) {
var myList;
// This is genuine Javascript try/catch, in case JSON.parse() throws.
try {
myList = JSON.parse(data);
}
catch (error) {
return $.Deferred().reject(error).promise();//must return a promise because that's what the caller expects, whatever happens.
}
//Now use `myList.reduce()` to build a promise chain from the array `myList` and the items it contains.
var promise = myList.reduce(function(promise, item) {
return promise.then(function(arr) {
return callWebService(item).then(function(result) {
arr.push(result);
return arr;
}, function(jqXHR, textStatus, errorThrown) {
if(checkIfContinuable(errorThrown)) {
return $.when(arr); // return a resolved jQuery promise to put promise chain back on the success path.
} else {
return new Error(textStatus);//Although the error state will be naturally propagated, it's generally better to pass on a single js Error object rather than the three-part jqXHR, textStatus, errorThrown set.
}
});
});
}, $.when([])) // starter promise for the reduction, resolved with an empty array
// At this point, `promise` is a promise of an array of results.
return promise.then(null, failWithTerribleError);
});
}
Notes:
An overall function wrapper is assumed, function getWebServiceResults() {...}.
callWebService() is assumed to accept item - ie the contents of each element of myList.
To do its job, checkIfContinuable() must accept at least one argument. It is assumed to accept errorThrown but might equally accept jqXHR or textStatus.

how to make $.when execute when one of JSON request fail

I'm using the below code to get JSON from multiple urls. However, when one of the URL failed or get 404 response the function the execute doesn't work. I read the jquery doc and I know "then" should execute no matter one of the call has failed.
var data = {};
var calls = [];
for (var i in funcs) {
calls.push(
$.getJSON(base_url+i,
(function(i) {
return function(d) {
data[i] = d;
};
}(i))
)
);
}
$.when.apply($,calls).then(function() {
do_something(data);
});
Take a look at always method. It will executed in both cases.
For example:
$.when.apply($, calls).always(function() {
alert('Resolved or rejected');
});
In response to successful transaction, arguments are same as .done() (ie. a = data, b = jqXHR) and for failed transactions the arguments are same as .fail() (ie. a = jqXHR, b = errorThrown). (c)
I read the jquery doc and I know "then" should execute no matter one of the call has failed.
Nope, the promise only gets fulfilled if all of the passed objects are fulfilled. If one of them fails, the result will get rejected.
is there a way to make do_something(data); execute no matter if it failed or not.
You could use .always:
// doSomething waits until all are fulfilled or one is rejected
$.when.apply($,calls).always(do_something);
Yet, you probably want to execute the callback when all calls are resolved (no matter if fulfilled or rejected) - like allSettled does in Q. With jQuery, you have to work around a little:
var calls = $.map(funcs, function(_, i) {
var d = new $.Deferred;
$.getJSON(base_url+i).done(function(r/*…*/) {
d.resolve(i, r);
}, function(/*…*/) {
d.resolve();
});
return d.promise();
});
$.when.apply($, calls).then(function() {
var data = {};
for (var i=0; i<arguments.length; i++)
data[arguments[i][0]] = arguments[i][1];
do_something(data);
});
As jQuery docs the .then function takes two (or three) arguments in your case deferred.then( doneCallbacks, failCallbacks )
So you need to specify the second function to handle the failing request.
i.e.
$.when.apply($,calls).then(function() {
alerT('ok');
}, function() {
alert('fail');
});
Here a simple fiddle: http://jsfiddle.net/rrMwr/
Hope this helps
You need to use the second parameter of deferred.then() (documentation):
$.when.apply($, calls).then(function() {
do_something(data);
}, function() {
// something failed
});
If you want to call the same callback regardless of successes and failures, use deferred.always() (documentation):
$.when.apply($, calls).always(function() {
do_something(data);
});
It is also worth reading the jQuery.when() documentation, which explains the aggregation when multiple deferred objects are passed.

Categories