AngularJS $q.all() results are null
I'm trying to implement a $q.all to run some functions and then return all the outputs into a function attached to the .then at the end.
At the moment the promises look like they're calling in the correct order and the $all .then is occurring at the end, but the results variable comes back with an array of nulls (one for each promise in the $q.all)
JS Fiddle can be found at http://jsfiddle.net/QqKuk/120/ and I'm using angular 1.0.1
The below is a simplified example of the code I have.
Here is my html, simply just there to display some debug text and the output.
<div ng-controller="MyCtrl">
<p>{{fromThen}}</p>
<p>{{fromThen2}}</p>
<p>{{runOrder}}</p>
</div>
and here is my controller, in reality logOne, logTwo, and logThree aren't going to be identical functions.
var myApp = angular.module('myApp', []);
function MyCtrl($scope, $q, $timeout) {
var logOne = function (value) {
$scope.fromThen = $scope.fromThen + value;
var deffered = $q.defer();
deffered.promise.then( function() {
$scope.runOrder = $scope.runOrder + '.logOne()';
$scope.fromThen = $scope.fromThen + value.toUpperCase();
deffered.resolve(value);
return deffered.promise;
});
deffered.resolve();
};
var logTwo = function (value) {
$scope.fromThen = $scope.fromThen + value;
var deffered = $q.defer();
deffered.promise.then( function() {
$scope.runOrder = $scope.runOrder + '.logTwo()';
$scope.fromThen = $scope.fromThen + value.toUpperCase();
deffered.resolve(value);
return deffered.promise;
});
deffered.resolve();
};
var logThree = function (value) {
$scope.fromThen = $scope.fromThen + value;
var deffered = $q.defer();
deffered.promise.then( function() {
$scope.runOrder = $scope.runOrder + '.logThree()';
$scope.fromThen = $scope.fromThen + value.toUpperCase();
deffered.resolve(value);
return deffered.promise;
});
deffered.resolve();
};
$scope.fromThen = '';
$scope.fromThen2 = 'No Value';
$scope.runOrder = '';
$q.all([logOne('One'), logTwo('Two'), logThree('Three')])
.then(function(results) {
$scope.runOrder = $scope.runOrder + '.then';
$scope.fromThen2 = results;
});
}
The output I'm getting is
OneTwoThreeONETWOTHREE [null,null,null] .logOne().logTwo().logThree().then
Which to me looks like things are calling in the correct order, so I'm confused why I'm getting nulls in the return value. Am I using the defer.resolve(value) incorrectly?
I've looked at some of the other examples on here but I haven't been able to work out why I'm not getting a result.
Thanks for any help you can give. Since this is also my first post, any tips on what information i should also include (or didn't need to include) would also be appreciated.
Thanks. Neil
Your issue is that you're not returning your promises from the log functions themselves for $q.all
to follow. You're resolving the promises and returning them somewhere, but not to anywhere that's listening. Functions inside calls to .then
are called by $q
and the return values are sent to the resolution callbacks for the promises .then
itself returns. Your promising functions should take the form:
var function = doSomthingDeferred(data) {
var deferred = $q.defer();
doSomethingDeferredWith(data).then(function(deferredResult) {
var processedResult = processDeferredResult(deferredResult);
deferred.resolve(processedResult);
});
return deferred.promise;
}
Alternatively
var function = doSomthingDeferred(data) {
return doSomethingDeferredWith(data).then(function(deferredResult) {
var processedResult = processDeferredResult(deferredResult);
return processedResult;
});
}
In your case, when you doSomethingDeferredWith(data)
you:
function doSomethingDeferredWith(data) {
var deferredMore = $q.defer();
$scope.fromThen += data;
deferredMore.resolve($scope.fromThen);
This particular action doesn't really need to be deferred, it finishes immediately, but if you were querying an $http
based service, then you'd get your deferredMore
promise back:
return deferredMore.promise;
}
Then, after you're done doing that, you're going to get some result as a parameter to a function referenced in a call to .then
on a promise
like the one returned from doSomethingDeferredWith
:
doSomethingDeferredWith(data).then(function(deferredResult) {
Now, because of the way $q
works, the call to doSomethingDeferredWith(data)
returns a promise, .then
is called on that promise and the function passed in is queued up, but is not executed until the current script loop ends . This means that .then
is called, the function is queued, and then doSomethingDeferred
continues executing, returns, and its calling function then continues executing until the call stack has cleared. Only after that does $q
have the opportunity to come back and run all of the callbacks for resolved promises.
In your code, doSomethingDeferred
, the various log***
functions, do not actually return a promise. They return undefined
. If you instead return the promise we created and will resolve when $q
runs the callback instead of at the end of doSomethingDeferred
, you'll get your data in the callback for $q.all
.
To fix your code, change the deffered.resolve();
calls at the end of each of your log files to return deffered.promise;
Then, the return values for the log functions won't be undefined
, they'll be promises that $q
can follow and run a callback on all three of their .resolve
calls at once after all three calls are made, setting your $scope.runFrom2
value to an array of ['One','Two','Three']
as each individual promise resolves with the value
from the closure frame of the deferring function.
tl;dr Version
Change the deffered.resolve();
calls at the end of each of your log files to return deffered.promise;