I would like that the for loop will be executed and then the result will be sent over the next function:
FindIdsRequests = function(){
results = $scope.requests
var deferred = $q.defer();
var promise = deferred.promise;
var array = []
for (var i in results) {
promise = promise.then(function(){
array.push(results[i].uid)
})
return promise
}
return promise.then(function(){
return array
})
}
$scope.ConfirmRequests = function(){
//alert('req'+JSON.stringify($scope.requests))
FindIdsRequests().then(function(array){
alert('arr'+JSON.stringify(array))
})
})
the FindIdsRequests function should return the result of the for loop however there is no return (the alert is not printed so does not arrive there). Any idea?
Problems:
You are overwriting promise in each iteration of loop... so there is only ever one promise
There is no resolve of promise to ever trigger then() so the array will always be empty
You have a return in the loop so the loop will break on first iteration and never complete
If this were asynchronous i will not be what you think it is inside then() as it will have reached it's end before the promise resolves
I see no need to write this up in code since everything you are doing is synchronous and shows no need for promise in the first place
You can take advantages of $q insted of returning a promise like so:
$q.all([promise1, promise2]);
For example:
FindIdsRequests = function(){
var requests = $scope.requests;
var results = [];
var array = [];
for (var i in requests) {
var req = $http.get('http://someUrl.com/' + requests[i].uid);
array.push(req);
req.then(function (response) {
results.push(response.data);
});
}
return $q.all(array).then(function(){
return results;
});
}
This code will return a promisse of all promisses in the array to be resolved.
Ok thanks also to the comments of #charlietlf the following code works:
FindIdsRequests = function(){
results = $scope.requests
var deferred = $q.defer();
var promise = deferred.promise;
var array = []
for (var i in results) {
alert('www'+JSON.stringify(results[i].uid))
var obj = results[i].uid
promise = promise.then(function(){
array.push(results[i].uid)
})
//return promise
}
deferred.resolve(array)
return promise.then(function(){
alert('ee'+JSON.stringify(array))
return array
})
}
essentially I forgot to put the resolve.
Creating a Chain of Promises from an Array of Promises
Use $.when to create an empty promise and use a for loop to create a chain:
function chainPromiseArray(promiseArray){
var promise = $q.when();
var array = [];
for (var i=0; i < promiseArray.length; i++) {
promise = promise.then(function(){
var derivedPromise = promiseArray[i].then(function(result) {
//push data
array.push(result.data);
});
//return promise to chain
return derivedPromise;
});
};
var finalPromise = promise.then(function(){
return array;
});
return finalPromise;
};
The above example starts with an empty promise, then chains from that empty promise. The final promise will resolve to the array created by the chain.
Chaining promises
Because calling the .then method of a promise returns a new derived promise, it is easily possible to create a chain of promises. It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs.
--AngularJS $q Service API Reference -- chaining promises
The example executes the promises sequentially. To execute the promises in parallel use the $q.all method.
Your FindIdsRequests() function has no async elements in the code as shown so it can just be a synchronous function and, in fact, trying to use promises in it is just complicating a very simple operation:
// get an array of uid from $scope.requests
function FindIdsRequests() {
return $scope.requests.map(function(item) {
return item.uid;
});
});
$scope.ConfirmRequests = function() {
var ids = FindIdsRequests();
console.log('arr: '+ JSON.stringify(ids));
})
Related
I have the following angularJS function that get data by $resource ,it works and push this data to vm.data array, ,but when I try to get vm.data in controller i get null ,This last line of the following code print null value
function loadAll() {
Questiongroup.query({}).$promise.then(function(group){
console.log(group);
for(var i=0;i<group.length;i++){
var grouptitle=group[i].title
Question.questionsByQuestionGroup({id:group[i].id}).$promise.then(function(question){
vm.question = question;
for(var j=0;j<question.length;j++){
vm.data.push({ group: grouptitle, question: question[j].question , questiontype: question[j].type });
console.log(vm.data) //this line print correct data
}
});
}
});
}
loadAll();
console.log(vm.data); //This line print null
The reason that last console.log prints null is that it executes before the functions given to the .then methods. The functions given as arguements to .then methods are invoked by the $q service after the data returns from the server.
The loadAll function should return a derived promise which can be used to retrieve the vm.data after being assembled. This involves chaining promises and using the $q.all method.
function loadAll() {
var topPromise = Questiongroup.query({}).$promise;
var derivedPromise = topPromise.then(function(group){
var promiseList = [];
console.log(group);
for(var i=0;i<group.length;i++){
var itemPromise = getItemPromise(group[i]);
promiseList.push(itemPromise);
};
//return $q.all promise for chaining
return $q.all(promiseList);
});
//return promise
return derivedPromise;
};
Then use the returned promise.
var loadAllPromise = loadAll();
loadAllPromise.then(function (vmData) {
console.log(vmData);
});
The above example shows how to create a promise that returns an array of data from an array of promises. It is intuitively obvious to extend that to multiple levels of iteration.
AngularJS $q.all also works with hashes.
function loadAll() {
var topPromise = Questiongroup.query({}).$promise;
var derivedPromise = topPromise.then(function(group){
var promiseHash = {};
console.log(group);
for(var i=0;i<group.length;i++){
var itemPromise = getItemPromise(group[i]);
promiseHash[group[i].id] = itemPromise;
};
//return $q.all promise for chaining
return $q.all(promiseHash);
});
//return promise
return derivedPromise;
};
The above example returns a promise that contains a hash (associative array) of data.
loadAllPromise.then(function (dataHash) {
console.log(dataHash);
});
From the Docs:
all(promises);
Combines multiple promises into a single promise that is resolved when all of the input promises are resolved.
Parameters
An array or hash of promises.
Returns
Returns a single promise that will be resolved with an array/hash of values, each value corresponding to the promise at the same index/key in the promises array/hash. If any of the promises is resolved with a rejection, this resulting promise will be rejected with the same rejection value.
--AngularJS $q Service API Reference -- $q.all
How to make foreach loop synchronous in AngularJS
var articles = arg;
articles.forEach(function(data){
var promises = [fetchImg(data), fetchUser(data)];
$q.all(promises).then(function (res) {
finalData.push(res[1]);
});
});
return finalData;
I want finalData array to be returned only after the forEach loop gets over. Is there any way to chain it with promises? Something that will execute the foreach loop first and then return the array after the loop is over?
Chaining promises from a foreach loop
Promises are chained by returning values (or a promise) to the handler function in the .then method. Multiple promises are consolidated by using $q.all which itself returns a chainable promise.
function fetchUsers(arg) {
var articles = arg;
var promises = articles.map(function(a){
var subPromises = [fetchImg(a), fetchUser(a)];
return $q.all(subPromises).then(function (res) {
//return for chaining
return {img: res[0], user: res[1]};
});
});
//consolidate promises
var finalPromise = $q.all(promises);
return finalPromise;
};
Because calling the .then method of a promise returns a new derived promise, it is easily possible to create a chain of promises. It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain.1
The returned promise will either resolve fulfilled with an array of users or will resolve rejected with the first error. Final resolution is retrieved with the promise's .then and .catch methods.
fetchUsers(args)
.then ( function onFulfilled(objArray) {
$scope.users = objArray.map(x => x.user);
return objArray;
}).catch ( function onRejected(response) {
console.log("ERROR: ", response);
throw response
})
;
The $q.defer Anti-pattern
The problem with using $q.defer() is that it breaks the promise chain, loses error information, and can create memory leaks when errors are not handled properly. For more information on that see, AngularJS Is this a “Deferred Antipattern”?.
You can modify your code like this:
function fetArticles(arg) {
var articles = arg;
var promises = [], finalData = [];
var deferred = $q.defer();
articles.forEach(function(data) {
var userPromise = fetchUser(data);
userPromise.then(function (res) {
finalData.push(res[1]);
});
promises.push(fetchImg(data));
promises.push(userPrommise);
});
$q.all(promises).then(function() {
deferred.resolve({finalData: finalData, foo: "bar"});
});
return deferred.promise;
}
Now, call this method and register a final callback:
fetArticles(arg).then(function(data) {
console.log("finalData: ", data.finalData, data.foo === "bar");
});
Issue in resolving promises in angular. I want promise.then method to execute after the execution of getUsersRegister(-). But, the method present inside the promise.then() is executing first before the former method.
Is that the way I wrote the promise concept is right or need to modify something else.
function getMysers(){
var promise = getUsersRegister(teamId);
promise.then(friendsService.myFriends.query({},function(data){
var users = data;
}));
}
function getUsersRegister(teamId){
var q = $q.defer();
teamService.fetchData(function(data){
q.resolve(data);
var results = data;
});
return q.promise;
}
I call a function that returns a list with ID that i want to use in chained call. Everything seems to work until i want to read all those objects that are returned.. Those are promises but i cannot find out why i cannot resolve them.
//Get bubbles and then it calls another function getBubbleMessage with result from previous and last getBubbleMessage returns an array of promises.
$scope.loadStartPage = function () {
$scope.getBubblesThatUserHasAccessTo().then($scope.getBubbleMessage).then(function (data) {
$log.info("INFO:: " + data);
$scope.bubblesWithMessages = data;
});
};
$scope.getBubblesThatUserHasAccessTo = function () {
var deferred = $q.defer();
BubblesService.getBubblesUserAccess().then(function (result) {
deferred.resolve(result);
});
return deferred.promise;
};
This function is gettings some things that we need to resolve messages connected to those id:s that above service is returning
$scope.getBubblesThatUserHasAccessTo = function () {
var deferred = $q.defer();
BubblesService.getBubblesUserAccess().then(function (result) {
deferred.resolve(result);
});
return deferred.promise;
};
This function get alls messages and returns promise objects - and these i cannot resolve??
$scope.getBubbleMessage = function (data) {
var deferred = $q.defer();
var promises = [];
angular.forEach(data, function (item) {
$log.info("DDD" + item.name);
var promise = BubblesService.getBubbleMessages(item.id, 0, 1);
promises.push(promise);
});
//return $q.all([promises]);
$q.all([promises]).then(function (result) {
$log.info(result);
return result[0];
});
};
Above function returns an array of 60 objects..
In the end i want to have a new object that i use in my ng-repeat on page. I really think this is something todo that im new to angular and promises.... but after a couple of hours of trying to fix this i really need help :)
$q.all takes an array of promise. Here, you are doing $q.all([myPromises]), which resolve instantly, because '[myPromise]' is an array and not a promise (you give an array parameter with first and only element is an array of promise when you should simply use the promise array. So [] and not [[]]). Second issue : you are not returning this promise in the parent function.
You should simply change the block
$q.all([promises]).then(function (result) {
$log.info(result);
return result[0];
});
with
return $q.all(promises);
Which will resolve with an array of resolved for each promise in the array.
I am using the AngularJS implementation of $q.
Given the following functions:
doTask1: function ($scope) {
var defer = $q.defer();
$http.get('/abc')
.success(function (data) {
defer.resolve();
})
.error(function () {
defer.reject();
});
return defer.promise;
},
doTask2: function ($scope) {
var defer = $q.defer();
var x = 99;
return defer.promise;
},
I know I can delay execution of another function like this:
os.doTask1()
.then(function () {
doTask3();
});
I would like to start off doTask1 and doTask2 at the same time.
Is there a way I can do this and still delay execution so that
doTask3() will not execute until doTask1 and doTask2 have ended
with success.
$q.all is what you're looking for. (Documentation)
$q.all takes multiple promises, either in an array or object, and returns a brand new promise that will be resolve when all the passed-in promises are resolved. If you passed in an array of promises, the new promise is resolved with an array of values which correspond to the promises:
$q.all([doTask1(), doTask2()]).then(function(results) {
// results[0] == result of doTask1
// results[1] == result of doTask2
doTask3();
});
If you passed in an object of key-promise pairs, it will be resolved with an object with keys that match the object you passed in, each value nicely corresponding to the value of that key's resolved promise:
$q.all({one: doTask1(), two: doTask2()}).then(function(results) {
// results.one == result of doTask1
// results.two == result of doTask1
doTask3();
});
Because of promise chaining (e.g., when you return a promise from a then function, it creates a new promise that resolves to the resolved value of the promise you returned), you can do some cool stuff:
var bothPromises = $q.all([doTask1(), doTask2()]);
var task3Promise = bothPromises.then(function(results) {
var result1 = results[0];
var result2 = results[1];
return doTask3(result1, result2);
});
task3Promise.then(function(resultOfDoTask3) { ... });
It is worth nothing that if any of the promises passed to $q.all is rejected, the promise that is returned will also be rejected. See the Angular $q documentation for more information.
This is (barely) tangential to the question, but I think it's a neat trick: if you happen to use CoffeeScript, you can use destructuring to get to the promises' resolved values.
For arrays:
$q.all([doTask1(), doTask2()]).then ([result1, result2]) ->
# ...
And for objects
$q.all(one: doTask1(), two: doTask2()).then ({one, two}) ->
# note that you have to use the same key names
# as in the object you passed to `$q.all`