Q require("asap") and getting related data in angularjs - javascript

I am using q.js for the first time with Angular.js and I have some code below where I am trying to get related info on a foreign key (each Team object has a GroupId foreign key that links to a Group object) :
new Team().$getAll().then(function (data)
{
angular.forEach(data.value, function(value, key) {
// We create our own promise to return
var deferred = $q.defer();
$http.get('http://mycloudapp.cloudapp.net/odata/Groups(' + value.GroupId + ')').then(function(group) {
angular.forEach(group, function(value, key) {
alert(value.GroupName);
});
//set the group here somehow?
$scope.teams = data.value;
// resolve the promise
deferred.resolve(group);
}, function getGroupError() { deferred.reject();
});
});
});
When I run this code I get the error :
ReferenceError: require is not defined
var asap = require("asap");
I have tried adding require.js and asap.js but that does not help, what do I need to do in this situation?
Also, I have not yet got to the stage where I actually get the group info and add it to $scope.teams, can anybody help me out with what syntax I would use to do this?

You will need to use the version of Q built for use in a web browser. The version checked in on Github is in development and unstable, so be sure to grab the latest release, 0.9.7. See “Getting Started” on https://github.com/kriskowal/q

First, with angular you will probably want to use the built in promise service and inject $q into your controller/service.
Second, it seems you want to resolve a bunch of promises in a function and the $q.all is the way to go. An example might be:
function myCtrl($q, $scope) {
function getKey(groupId) {
// We create our own promise to return
var deferred = $q.defer();
$http.get('http://mycloudapp.cloudapp.net/odata/Groups(' + groupId + ')').then(function(group) {
// resolve the promise
deferred.resolve(group);
}, function getGroupError() { deferred.reject();
});
return defferred.promise;
}
new Team().$getAll().then(function (data)
{
promiseArray = [];
angular.forEach(data.value, function(value, key) {
var p = getKey(value);
promiseArray.push(p);
});
$q.all(promiseArray).then(function (data) {
// data will have an array of promises returned after all complete.
$scope.teams = data; // just depends exactly what you are doing.
});
}
}
I posted a question about this prior that might help.

Related

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.

Promises for loop angular confused

I understand using promises in simple scenarios but currently really confused on how to implement something when using a for loop and some updates to local sqlite database.
Code is as follows
surveyDataLayer.getSurveysToUpload().then(function(surveys) {
var q = $q.defer();
for (var item in surveys) {
var survey = surveys[item];
// created as a closure so i can pass in the current item due to async process
(function(survey) {
ajaxserviceAPI.postSurvey(survey).then(function(response) {
//from response update local database
surveyDataLayer.setLocalSurveyServerId(survey, response.result).then(function() {
q.resolve; // resolve promise - tried only doing this when last record also
})
});
})(survey) //pass in current survey used to pass in item into closure
}
return q.promise;
}).then(function() {
alert('Done'); // This never gets run
});
Any help or assistance would be appreciated. I'm probably struggling on how best to do async calls within loop which does another async call to update and then continue once completed.
at least promises have got me out of callback hell.
Cheers
This answer will get you laid at JS conferences (no guarantees though)
surveyDataLayer.getSurveysToUpload().then(function(surveys) {
return Promise.all(Object.keys(surveys).map(function(key) {
var survey = surveys[key];
return ajaxserviceAPI.postSurvey(survey).then(function(response){
return surveyDataLayer.setLocalSurveyServerId(survey, response.result);
});
}));
}).then(function() {
alert('Done');
});
This should work (explanations in comments):
surveyDataLayer.getSurveysToUpload().then(function(surveys) {
// array to store promises
var promises = [];
for (var item in surveys) {
var survey = surveys[item];
// created as a closure so i can pass in the current item due to async process
(function(survey) {
var promise = ajaxserviceAPI.postSurvey(survey).then(function(response){
//returning this promise (I hope it's a promise) will replace the promise created by *then*
return surveyDataLayer.setLocalSurveyServerId(survey, response.result);
});
promises.push(promise);
})(survey); //pass in current survey used to pass in item into closure
}
// wait for all promises to resolve. If one fails nothing resolves.
return $q.all(promises);
}).then(function() {
alert('Done');
});
Awesome tutorial: http://ponyfoo.com/articles/es6-promises-in-depth
You basically want to wait for all of them to finish before resolving getSurveysToUpload, yes? In that case, you can return $q.all() in your getSurveysToUpload().then()
For example (not guaranteed working code, but you should get an idea):
surveyDataLayer.getSurveysToUpload().then(function(surveys) {
var promises = [];
// This type of loop will not work in older IEs, if that's of any consideration to you
for (var item in surveys) {
var survey = surveys[item];
promises.push(ajaxserviceAPI.postSurvey(survey));
}
var allPromise = $q.all(promises)
.then(function(responses) {
// Again, we want to wait for the completion of all setLocalSurveyServerId calls
var promises = [];
for (var index = 0; index < responses.length; index++) {
var response = responses[index];
promises.push(surveyDataLayer.setLocalSurveyServerId(survey, response.result));
}
return $q.all(promises);
});
return allPromise;
}).then(function() {
alert('Done'); // This never gets run
});

Angular promise on multiple $http

I am trying to do multiple $http call and my code looks something like this:
var data = ["data1","data2","data3"..."data10"];
for(var i=0;i<data.length;i++){
$http.get("http://example.com/"+data[i]).success(function(data){
console.log("success");
}).error(function(){
console.log("error");
});
}
How can I have the promise to know all $http call is successfull? If anyone of it fail, will perform some action.
You could also use $q.all() method.
So, from your code:
var data = ["data1","data2","data3"..."data10"];
for(var i=0;i<data.length;i++){
$http.get("http://example.com/"+data[i]).success(function(data){
console.log("success");
}).error(function(){
console.log("error");
});
}
You could do:
var promises = [];
data.forEach(function(d) {
promises.push($http.get('/example.com/' + d))
});
$q.all(promises).then(function(results){
results.forEach(function(data,status,headers,config){
console.log(data,status,headers,config);
})
}),
This above basically means execute whole requests and set the behaviour when all have got completed.
On previous comment:
Using status you could get to know if any have gone wrong. Also you could set up a different config for each request if needed (maybe timeouts, for example).
If anyone of it fail, will perform some action.
From docs which are also based on A+ specs:
$q.all(successCallback, errorCallback, notifyCallback);
If you are looking to break out on the first error then you need to make your for loop synchronous like here: Angular synchronous http loop to update progress bar
var data = ["data1", "data2", "data3", "data10"];
$scope.doneLoading = false;
var promise = $q.all(null);
angular.forEach(data, function(url){
promise = promise.then(function(){
return $http.get("http://example.com/" + data[i])
.then(function (response) {
$scope.data = response.data;
})
.catch(function (response) {
$scope.error = response.status;
});
});
});
promise.then(function(){
//This is run after all of your HTTP requests are done
$scope.doneLoading = true;
});
If you want it to be asynchronous then: How to bundle Angular $http.get() calls?
app.controller("AppCtrl", function ($scope, $http, $q) {
var data = ["data1", "data2", "data3", "data10"];
$q.all([
for(var i = 0;i < data.length;i++) {
$http.get("http://example.com/" + data[i])
.then(function (response) {
$scope.data= response.data;
})
.catch(function (response) {
console.error('dataerror', response.status, response.data);
break;
})
.finally(function () {
console.log("finally finished data");
});
}
]).
then(function (results) {
/* your logic here */
});
};
This article is pretty good as well: http://chariotsolutions.com/blog/post/angularjs-corner-using-promises-q-handle-asynchronous-calls/
Accepted answer is okay, but is still a bit ugly. You have an array of things you want to send.. instead of using a for loop, why not use Array.prototype.map?
var data = ["data1","data2","data3"..."data10"];
for(var i=0;i<data.length;i++){
$http.get("http://example.com/"+data[i]).success(function(data){
console.log("success");
}).error(function(){
console.log("error");
});
}
This becomes
var data = ['data1', 'data2', 'data3', ...., 'data10']
var promises = data.map(function(datum) {
return $http.get('http://example.com/' + datum)
})
var taskCompletion = $q.all(promises)
// Usually, you would want to return taskCompletion at this point,
// but for sake of example
taskCompletion.then(function(responses) {
responses.forEach(function(response) {
console.log(response)
})
})
This uses a higher order function so you don't have to use a for loop, looks a lot easier on the eyes as well. Otherwise, it behaves the same as the other examples posted, so this is a purely aesthetical change.
One word of warning on success vs error - success and error are more like callbacks and are warnings that you don't know how a promise works / aren't using it correctly. Promises then and catch will chain and return a new promise encapsulating the chain thus far, which is very beneficial. In addition, using success and error (anywhere else other than the call site of $http) is a smell, because it means you're relying explicitly on a Angular HTTP promise rather than any A+ compliant promise.
In other words, try not to use success/error - there is rarely a reason for them and they almost always indicate a code smell because they introduce side effects.
With regards to your comment:
I have did my own very simple experiment on $q.all. But it only trigger when all request is success. If one if it fail, nothing happen.
This is because the contract of all is that it either resolves if every promise was a success, or rejects if at least one was a failure.
Unfortunately, Angular's built in $q service only has all; if you want to have rejected promises not cause the resultant promise to reject, then you will need to use allSettled, which is present in most major promise libraries (like Bluebird and the original Q by kriskowal). The other alternative is to roll your own (but I would suggest Bluebird).

How to know when angular has finished deferring

Hi I am struggling to understand Angular deffering and promises.
I would like to know when all promises have been completed so i can hide a "loading" message.
So here is my code example:
$scope.buildTeam = function () {
$scope.Message = "loading...";
var deferred = $q.defer();
var promiseList = [];
c = $scope.teamModels[0];
for (index = 0; index < c.quantity; ++index) {
var assignment = new teamAssignment(c.assignmentTypeId, $scope.parentAssignment.AssignmentId, c.description);
promiseList.push(departmentService.addAssignment(assignment).then(function (result) {
//added lead/captain. Now insert visually on page
insertToScope($scope, result);
}));
}
}
$q.all(promiseList).then(function () {
deferred.resolve();
$scope.Message = "Loaded.";
});
}
The problem is Have is that $scope.Message shows "Loaded" well before the data has been inserted onto the page in the case of a large data pull.
Could it be that i also need to defer insertToScope?
InsertToScope simply reads:
function insertToScope($scope, a) {
//get root scope
var rs = angular.element("#ngRootContainer").scope();
rs.parentAssig.push(a);
}
And the department service looks like this:
departmentModule.factory('departmentService', function ($http, $q) {
return {
addAssignment: function(assignment){
var deferred = $q.defer();
$http.post('/Department/addAssignmentReturnAssignmentRow', assignment).success(deferred.resolve).error(deferred.reject);
return deferred.promise;
}
});
So my question is, what do i need to do to call a function only after all the promises are done?
Thank you in advance for your feedback.
$q.all should do what you need. If your code doesn't work, I see two options:
You pass in a promiseList that does not contain all promises you want to wait for. Debug your code and check that all promises are passed in. Make sure it's an array you pass as the first parameter.
The promises you pass in resolve before you expect them to resolve. If you log 'A' in insertToScope, and 'B' in the then of your $q.all, you should see
A
...
A
B
in the console.
Here is a simple plunkr that shows how to use $q.all(): http://plnkr.co/edit/9QH9Y0w7DG0WCouMuX82?p=preview

Promise API - combining results of 2 asynchronous call

With promise API, how to send two asynchronous request in parallel, and resolve the combined result as the response.
var get = function(id){
var res1, res2;
var deferred = $q.defer();
Db.get(id, "abc")
.then(function (d) {
//deferred.resolve(d));
res1 = d;
}, function (e) {
//error
});
Db.get(id, "def")
.then(function (d) {
//deferred.resolve(d));
res2 = d;
}, function (e) {
//error
});
//?????? how to return {res1:res1 , res2: res2}
return deferred.promise;
};
now, when I call get() like
get(123).then(function(d)){
// d= {res1: res1, res2: res2}
},
...
I need to get the combined result as indicated. How to do this with Angular promise API?
As #Matt said, you need to use $q.all, but the usage isn't quite right. AngularJS doesn't support .done and .fail and they don't work quite like that anyway in that there's no such thing as a promise for multiple values, instead you just have a promise for an array.
If you were writing this using the full Q we would write:
var get = function (id) {
return Q.all([Db.get(id, "abc"), Db.get(id, "def")])
.spread(function (res1, res2) {
return {res1: res1, res2: res2};
});//the error case is handled automatically
};
In this case, .spread acts like .then except that it spreads the results of an array for a promise out over the arguments of its onFulfilled function. To change this to use the promise methods from AngularJS we just need to make do without .spread. This leads to the following solution:
var get = function (id) {
return $q.all([Db.get(id, "abc"), Db.get(id, "def")])
.then(function (res) {
return {res1: res[0], res2: res[1]};
});//the error case is handled automatically
};
The beauty of this is that we are freed from handling all the nitty grity of error propagation and storing the partial results because .then acts as a filter. If you leave out the error handler, it automatically propagates any errors. This means that if either of the input promises are rejected, the result will be rejected. If both promises are fulfilled successfully, res is the array of those resolution values.
I have something to add to #ForbesLindesay answer.
In our case, we wanted partial results: if a request failed (eg. server has an hiccup, we request something deleted by somebody else, etc.), we still want to collect the valid responses, and to report the errors.
I found out that we need to handle success and failure on each promise, returning a value that will be collected by $q.all.
Here is our code, simplified and made generic ('item'...):
var promiseList = _.map(itemList, function(item)
{
return DataService.getISubtems(item.id)
.then(
function(response)
{
var subItems = response.data;
$log.info('Received sub-item list;' + subItems.length + ';items received');
return subItems;
},
function(reason)
{
$log.warn('Sub-item list not received for item;' + item.name + ';' + item.id);
$scope.errorList.push('Sub-item list not received for item "' + item.name + '"');
}
);
});
$q.all(promiseList)
.then(function(itemArray)
{
// We get an array of arrays interleaved with undefined value when an error was raised.
// That's because error handling doesn't return anything, ie. returns undefined.
// We remove these undefined values then put all operations at the same level.
var allOperations = _(operationArray).reject(_.isUndefined).flatten().value();
if ($scope.errorList.length > 0)
{
NotificationService.warning('Items Fetching', 'Errors while getting item list:\n' +
$scope.errorList.join('\n'));
}
$scope._onItemListReceived(allItems);
});
Note that we use Lodash (_.map, _.flatten, _.reject, _.isUndefined) but I think the usage is quite clear (that's the nice point of this library!).
You can use the angular-q-spread library and then use the same code as #ForbesLindesay's first example:
// The module needs $q-spread as a dependency:
// angular.module('…', ['$q-spread']);
var get = function (id) {
return $q.all([Db.get(id, "abc"), Db.get(id, "def")])
.spread(function (res1, res2) {
return {res1: res1, res2: res2};
});
};

Categories