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;
}
Related
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));
})
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
I've been trying to write better code on my node.js server and after reading some blog posts like the following:
http://www.codelord.net/2015/09/24/$q-dot-defer-youre-doing-it-wrong/ (angular specific but same concept)
http://bahmutov.calepin.co/linking-promises.html
I am still not sure if I'm returning my data down the promise chain the "right way".
I cannot tell when it's appropriate to return or pass data down a promise like this
case 1
var promise = function () {
var defer = q.defer();
var myData = "hi"
defer.resolve(myData);
return d.promise;
};
or like this.
case 2
var promise = function () {
var myData = "hi"
return myData;
};
I'm assuming is that if I know something will be returned where it's not possible for the promise chain to break then use case 2 but if their is a change it could fail (i.e. it's returning data from a network call but the user is offline) then use case 1 so it can handle both cases. Is that correct or do I have a misunderstanding about how this flow/process works.
In both cases you are returning a result which is instantly known or computed, while you are wrapping it in a promise in the first case. Whether or not you want to do this depends on whether it should be transparent for the caller of your function if the result is computed asynchronously or not. If you return a promise you are free to change the implementation of your function later to compute or retrieve the result asynchronously.
Two hints:
You are not using a promise in case 2 so don't call your function promise.
In the first case you can just return q("hi"); to wrap the literal in a promise.
promise is for those who engage callback hell, which means your jobs are Asynchronous and chained
for a simple case like $.ajax().success(callback1).fail(callback2) is type of promise
3.your case is not Asynchronous, it might be like this:
var promise1 = function() {
//async get file name
}
var promise2 = function(filename) {
var defer = q.defer();
var myData = "hi"
//async
fs.readFile(filename, 'utf-8', function(err, data) {
if (err) {
defer.reject(err);
} else {
defer.resolve(myData);
}
}
}
return d.promise;
};
var promise3 = function(data) {
//use data do something
}
var onError(err) {
console.log(err);
}
var onDone(result) {
console.log(result)
}
//your logic might look like
promise1.then(promise2).then(promise3).catch(onError).done(onDone);
I'm trying to get my head around promises in JavaScript (in particular AngularJS).
I have a function in a service, let's call it fooService, that checks if we've loaded some data. If it has, I just want it to return, and if we haven't, we need to load the data and return a promise:
this.update = function(data_loaded) {
if (data_loaded) return; // We've loaded the data, no need to update
var promise = Restangular.all('someBase').customGet('foo/bar').then(function(data) {
// Do something with the data here
}
return promise;
}
I have another function that then calls the update function of fooService like so:
fooService.update(data_loaded).then(function() {
// Do something here when update is finished
})
My issue here is that if we don't need to load the data in the update function, a promise isn't returned, so the .then() is not called in my other function. What should the approach be here - basically I want to return a resolved promise immediately from the update() function if we do not need to get data from the Restangular call?
As your promise use the same syntax as the JavaScript native one, you could use and return an already resolved JavaScript promise : Promise.resolve()
return(Promise.resolve("MyReturnValue"));
The current accepted answer is overly complicated, and abuses the deferred anti pattern. Here is a simpler approach:
this.update = function(data_loaded) {
if (data_loaded) return $q.when(data); // We've loaded the data, no need to update
return Restangular.all('someBase').customGet('foo/bar')
.then(function(data) {
// Do something with the data here
});
};
Or, even further:
this._updatep = null;
this.update = function(data_loaded) { // cached
this._updatep = this._updatep || Restangular.all('someBase') // process in
.customGet('foo/bar'); //.then(..
return this._updatep;
};
AngularJS's $q service will help you here. It is much like Kris Kowal's Q promise library.
When you have an async method that may return a promise or value use the $q.when method. It will take what ever is passed to it, be it a promise or a value and create a promise that will be resolved/rejected based on the promise passed, or resolved if a value is passed.
$q.when( fooService.update(data_loaded) ).then(function(data){
//data will either be the data returned or the data
//passed through from the promise
})
and then in your update function return the data instead of just returning
if (data_loaded) return data_loaded;
Similar to Elo's answer, you can return an already resolved promise using the async/await syntax:
this.update = async (data_loaded) => {
if (data_loaded)
return await null; // Instead of null, you could also return something else
// like a string "Resolved" or an object { status: 200 }
else
return await OtherPromise();
}
You could use the $q.defer() like this:
this.update = function (data_loaded) {
var deferred = $q.defer();
if (data_loaded) {
deferred.resolve(null); // put something that your callback will know the data is loaded or just put your loaded data here.
} else {
Restangular.all('someBase').customGet('foo/bar').then(function(data) {
// Do something here when update is finished
deferred.resolve(data);
}
}
return deferred.promise;
};
Hope this helps.
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`