how to get the resolved value within promise in angularjs - angularjs

how to get the resolved value directly in angular promise?
I want to get the string "ok" in below function, not the promise object.
anybody help??
var getReturn = function() {
var defer = $q.defer();
var promise = defer.promise;
defer.resolve("ok");
return promise.then(function (value) {
console.log(value);
return(value);
});
};

Since the function is not asynchronous, there's no need for a promise. So the correct way doing so will be:
var getReturn = function() {
return "ok";
}
If you still want this as a promise you should do as follows:
var getReturn = function() {
var defer = $q.defer();
setTimeout(function() {
defer.resolve("ok");
},0)
return defer.promise;
};
And wherever you call the function do the then:
getReturn().then(function (value) {
console.log(value);
});

You can't actually do that when you are dealing with promises. Promises are meant to be awaited or deferred until the expected value is ready meaning you should get the value only in it's "then" block especially if it's an $http promise.

Related

Angular $q returning resolved promise

I have a variable this.eligible which I would like to assign to the value of a returned promise instead of the actual promise object.
userService
this.eligible = this.sweepstakesService.checkUser(data);
sweepstakesService
checkUser({profileId}) {
var deferred = this.$q.defer();
var id = profileId.replace(/[{}]/g, "");
this.$q.when(this.getGuid(id)
.then(guid => this.determineEligibility(guid))
.catch(this.handleError))
.then(function(data){
deferred.resolve(data);
});
return deferred.promise;
}
getGuid(profileId){
return this.resourcesService.guid.save({id:profileId}).$promise.then(data => data.guid);
}
determineEligibility(response){
return this.resourcesService.eligibility.save({id:response}).$promise.then(data => data.isEligible);
}
handleError(response){
console.log(response);
}
Currently I'm returning Promise{$$state: Object} instead of the actual resolved value.
In order to access the result of a promise, you need to provide a callback to the then method on the promise object, which will be called asynchronously as soon as the result is available.
this.sweepstakesService.checkUser(data)
.then(function(value){
this.eligible = value;
});
When you're using promise, you're performing some asynchronous request, so you have to pass some callback function in order to retrieve your data, and wait for it.
You can use the $q.defer() promise manager, from the deferred API.
$q.defer() get 2 methods :
resolve(value) : which resolve our associated promise, by giving her the final value
reject(reason) : which resolve an promise error.
Don't forget that you are doing some asynchronous work...
Moreover, a good tips can be to save the current context into a variable, in order to bind your data.
Controller
(function(){
function Controller($scope, Service) {
//Save the current context of our controller
var self = this;
self.success = '';
self.fail = '';
//Declare our promise
var promise1 = Service.get(2);
var promise2 = Service.get(6);
promise1.then(function(response){
//Retrieve our response and set it to our success variable
//We use self as our Controller context
self.success = response;
}).catch(function(error){
self.success = error;
});
promise2.then(function(response){
self.fail = response;
}).catch(function(error){
//Retrieve our error and set it to our fail variable
self.fail = error;
});
}
angular
.module('app', [])
.controller('ctrl', Controller);
})();
Service
(function(){
function Service($http, $q) {
function get(n){
//using $q.defer() from deferred API
var defer = $q.defer();
//Simulate latency
setTimeout(function(){
n < 3
? defer.resolve(n)
: defer.reject('Error');
}, 1500);
//Return our promise
return defer.promise;
}
return {
get: get
}
}
angular
.module('app')
.factory('Service', Service);
})();
Then, you can instantiate your controller by using the controllerAs syntax.
HTML
<body ng-app='app' ng-controller='ctrl as self'>
<div>Success : {{self.success}}</div>
<div>Fail : {{self.fail}}</div>
</body>
You can see an example on this Working Plunker

Chaining API calls with $q.all

I don't know
how to add the returned data from resource to the promise array correctly. When I log it to the console its empty.
Here is my code:
var d = $q.defer();
var promises = [];
_.each(recipe.credentials, function(credential) {
APIService.save({route:'credential'},credential).$promise.then(function(data) {
promises.push(data)
});
});
$q.all(promises).then(function(data) {
console.log(data);
d.resolve();
});
return d.promise;
Updated Code:
var d = $q.defer();
var promises = recipe.credentials.map(function(credential) {
return APIService.save({route:'credential'},credential).$promise;
});
return $q.all(promises)
You should wrap promises when they're created, and don't forget the .catch handler:
$q.all(recipe.credentials.map(function(credential) {
return APIService.save({route:'credential'},credential).$promise;
})).then(function(data) {
console.log(data);
}).catch(function(reason) {
console.log(reason);
});
Also, most probably there's no need to create another defer - just return the result of $q.all into outer world.
P.S. I highly recommend reading this article about promises and their usage. )

angular $q, How to chain multiple promises within and after a for-loop

I want to have a for-loop which calls async functions each iteration.
After the for-loop I want to execute another code block, but not before all the previous calls in the for-loop have been resolved.
My problem at the moment is, that either the code-block after the for-loop is executed before all async calls have finished OR it is not executed at all.
The code part with the FOR-loop and the code block after it (for complete code, please see fiddle):
[..]
function outerFunction($q, $scope) {
var defer = $q.defer();
readSome($q,$scope).then(function() {
var promise = writeSome($q, $scope.testArray[0])
for (var i=1; i < $scope.testArray.length; i++) {
promise = promise.then(
angular.bind(null, writeSome, $q, $scope.testArray[i])
);
}
// this must not be called before all calls in for-loop have finished
promise = promise.then(function() {
return writeSome($q, "finish").then(function() {
console.log("resolve");
// resolving here after everything has been done, yey!
defer.resolve();
});
});
});
return defer.promise;
}
I've created a jsFiddle which can be found here http://jsfiddle.net/riemersebastian/B43u6/3/.
At the moment it looks like the execution order is fine (see the console output).
My guess is, that this is simply because every function call returns immediately without doing any real work. I have tried to delay the defer.resolve with setTimeout but failed (i.e. the last code block was never executed). You can see it in the outcommented block in the fiddle.
When I use the real functions which write to file and read from file, the last code block is executed before the last write operation finishes, which is not what I want.
Of course, the error could be in one of those read/write functions, but I would like to verify that there is nothing wrong with the code I have posted here.
What you need to use is $q.all which combines a number of promises into one which is only resolved when all the promises are resolved.
In your case you could do something like:
function outerFunction() {
var defer = $q.defer();
var promises = [];
function lastTask(){
writeSome('finish').then( function(){
defer.resolve();
});
}
angular.forEach( $scope.testArray, function(value){
promises.push(writeSome(value));
});
$q.all(promises).then(lastTask);
return defer.promise;
}
With the new ES7 you can have the same result in a much more straightforward way:
let promises = angular.forEach( $scope.testArray, function(value){
writeSome(value);
});
let results = await Promise.all(promises);
console.log(results);
You can use $q and 'reduce' together, to chain the promises.
function setAutoJoin() {
var deferred = $q.defer(), data;
var array = _.map(data, function(g){
return g.id;
});
function waitTillAllCalls(arr) {
return arr.reduce(function(deferred, email) {
return somePromisingFnWhichReturnsDeferredPromise(email);
}, deferred.resolve('done'));
}
waitTillAllCalls(array);
return deferred.promise;
}
This worked for me using the ES5 syntax
function outerFunction(bookings) {
var allDeferred = $q.defer();
var promises = [];
lodash.map(bookings, function(booking) {
var deferred = $q.defer();
var query = {
_id: booking.product[0].id,
populate: true
}
Stamplay.Object("product").get(query)
.then(function(res) {
booking.product[0] = res.data[0];
deferred.resolve(booking)
})
.catch(function(err) {
console.error(err);
deferred.reject(err);
});
promises.push(deferred.promise);
});
$q.all(promises)
.then(function(results) { allDeferred.resolve(results) })
.catch(function(err) { allDeferred.reject(results) });
return allDeferred.promise;
}

AngularJS: $q wait for all even when 1 rejected

I've been trying to wait for a couple of promises with Angular's $q but there seems to be no option to 'wait for all even when a promis is rejected'.
I've created an example (http://jsfiddle.net/Zenuka/pHEf9/21/) and I want a function to be executed when all promises are resolved/rejected, is that possible?
Something like:
$q.whenAllComplete(promises, function() {....})
EDIT: In the example you see that the second service fails and immediately after that the function in $q.all().then(..., function(){...}) is being executed. I want to wait for the fifth promise to be completed.
Ok, I've implemeted a basic version myself (I only want to wait for an array of promises). Anyone can extend this or create a cleaner version if they want to :-)
Check the jsfiddle to see it in action: http://jsfiddle.net/Zenuka/pHEf9/
angular.module('test').config(['$provide', function ($provide) {
$provide.decorator('$q', ['$delegate', function ($delegate) {
var $q = $delegate;
// Extention for q
$q.allSettled = $q.allSettled || function (promises) {
var deferred = $q.defer();
if (angular.isArray(promises)) {
var states = [];
var results = [];
var didAPromiseFail = false;
if (promises.length === 0) {
deferred.resolve(results);
return deferred.promise;
}
// First create an array for all promises with their state
angular.forEach(promises, function (promise, key) {
states[key] = false;
});
// Helper to check if all states are finished
var checkStates = function (states, results, deferred, failed) {
var allFinished = true;
angular.forEach(states, function (state, key) {
if (!state) {
allFinished = false;
}
});
if (allFinished) {
if (failed) {
deferred.reject(results);
} else {
deferred.resolve(results);
}
}
}
// Loop through the promises
// a second loop to be sure that checkStates is called when all states are set to false first
angular.forEach(promises, function (promise, key) {
$q.when(promise).then(function (result) {
states[key] = true;
results[key] = result;
checkStates(states, results, deferred, didAPromiseFail);
}, function (reason) {
states[key] = true;
results[key] = reason;
didAPromiseFail = true;
checkStates(states, results, deferred, didAPromiseFail);
});
});
} else {
throw 'allSettled can only handle an array of promises (for now)';
}
return deferred.promise;
};
return $q;
}]);
}]);
Analogous to how all() returns an array/hash of the resolved values, the allSettled() function from Kris Kowal's Q returns a collection of objects that look either like:
{ state: 'fulfilled', value: <resolved value> }
or:
{ state: 'rejected', reason: <rejection error> }
As this behavior is rather handy, I've ported the function to Angular.js's $q:
angular.module('your-module').config(['$provide', function ($provide) {
$provide.decorator('$q', ['$delegate', function ($delegate) {
var $q = $delegate;
$q.allSettled = $q.allSettled || function allSettled(promises) {
// Implementation of allSettled function from Kris Kowal's Q:
// https://github.com/kriskowal/q/wiki/API-Reference#promiseallsettled
var wrapped = angular.isArray(promises) ? [] : {};
angular.forEach(promises, function(promise, key) {
if (!wrapped.hasOwnProperty(key)) {
wrapped[key] = wrap(promise);
}
});
return $q.all(wrapped);
function wrap(promise) {
return $q.when(promise)
.then(function (value) {
return { state: 'fulfilled', value: value };
}, function (reason) {
return { state: 'rejected', reason: reason };
});
}
};
return $q;
}]);
}]);
Credit goes to:
Zenuka for the decorator code
Benjamin Gruenbaum for pointing me in the right direction
The all implementation from Angular.js source
The promise API in angularJS is based on https://github.com/kriskowal/q. I looked at API that Q provides and it had a method allSettled, but this method has not been exposed over the port that AngularJS uses. This is form the documentation
The all function returns a promise for an array of values. When this
promise is fulfilled, the array contains the fulfillment values of the
original promises, in the same order as those promises. If one of the
given promises is rejected, the returned promise is immediately
rejected, not waiting for the rest of the batch. If you want to wait
for all of the promises to either be fulfilled or rejected, you can
use allSettled.
I solved this same issue recently. This was the problem:
I had an array of promises to handle, promises
I wanted to get all the results, resolve or reject
I wanted the promises to run in parallel
This was how I solved the problem:
promises = promises.map(
promise => promise.catch(() => null)
);
$q.all(promises, results => {
// code to handle results
});
It's not a general fix, but it is simple and and easy to follow. Of course if any of your promises could resolve to null then you can't distinguish between that a rejection, but it works in many cases and you can always modify the catch function to work with the particular problem you're solving.
Thanks for the inspiration Zenuka, you can find my version at https://gist.github.com/JGarrido/8100714
Here it is, in it's current state:
.config( function($provide) {
$provide.decorator("$q", ["$delegate", function($delegate) {
var $q = $delegate;
$q.allComplete = function(promises) {
if(!angular.isArray(promises)) {
throw Error("$q.allComplete only accepts an array.");
}
var deferred = $q.defer();
var passed = 0;
var failed = 0;
var responses = [];
angular.forEach(promises, function(promise, index) {
promise
.then( function(result) {
console.info('done', result);
passed++;
responses.push(result);
})
.catch( function(result) {
console.error('err', result);
failed++;
responses.push(result);
})
.finally( function() {
if((passed + failed) == promises.length) {
console.log("COMPLETE: " + "passed = " + passed + ", failed = " + failed);
if(failed > 0) {
deferred.reject(responses);
} else {
deferred.resolve(responses);
}
}
})
;
});
return deferred.promise;
};
return $q;
}]);
})
A simpler approach to solving this problem.
$provide.decorator('$q', ['$delegate', function ($delegate) {
var $q = $delegate;
$q.allSettled = $q.allSettled || function (promises) {
var toSettle = [];
if (angular.isArray(promises)) {
angular.forEach(promises, function (promise, key) {
var dfd = $q.defer();
promise.then(dfd.resolve, dfd.resolve);
toSettle.push(dfd.promise);
});
}
return $q.all(toSettle);
};
return $q;
}]);
A simple solution would be to use catch() to handle any errors and stop rejections from propagating. You could do this by either not returning a value from catch() or by resolving using the error response and then handling errors in all(). This way $q.all() will always be executed. I've updated the fiddle with a very simple example: http://jsfiddle.net/pHEf9/125/
...
function handleError(response) {
console.log('Handle error');
}
// Create 5 promises
var promises = [];
var names = [];
for (var i = 1; i <= 5; i++) {
var willSucceed = true;
if (i == 2) willSucceed = false;
promises.push(
createPromise('Promise' + i, i, willSucceed).catch(handleError));
}
...
Be aware that if you don't return a value from within catch(), the array of resolved promises passed to all() will contain undefined for those errored elements.
just use finally
$q.all(tasks).finally(function() {
// do stuff
});

AngularJs scope attributes and promises

I have this code:
var geocode = function(value) {
var request;
.....
var dResult = Q.defer();
geocoder.geocode(request, function (results) {
dResult.resolve(results);
});
return dResult.promise;
};
var cancelWatch;
$scope.$watch('value', function (value) {
$timeout.cancel(update);
update = $timeout(function () {
$scope.geocodedResult = geocode(value);
}, 300);
});
in line 15 $scope.geocodedResult is a promise that sooner or later will become the result value and the scope should refresh. This unfortunately does not happen.
The code works if I do
geocode(value).then(function(result) {
$scope.geocodedResult = result;
$scope.$digest();
});
What am I doing wrong?
UPDATE:
I'm now trying to use only $q but I cannot get it to work:
this.getCurrentPosition = function () {
var dCurrentPosition = $q.defer();
if (currentPosition) {
dCurrentPosition.resolve(currentPosition);
} else {
navigator.geolocation.getCurrentPosition(function (cp) {
currentPosition = cp;
dCurrentPosition.resolve(currentPosition);
});
}
return dCurrentPosition.promise;
};
this.getCurrentLoc = function () {
return self.getCurrentPosition().then(function (currentPosition) {
return [currentPosition.coords.longitude, currentPosition.coords.latitude];
});
};
a breakpoint in
return [currentPosition.coords.longitude, currentPosition.coords.latitude];
will never get triggered while it works fine with Q
If you use Angular's $q instead of Q, it should work:
var dResult = $q.defer();
$q promises are recognized by the templating engine in angular, which means that in templates you can treat promises attached to a scope as if they were the resulting values. -- $q docs
So $scope.geocodedResult can be set to the $q promise, and when the promise is resolved, the scope property will automatically update.

Resources