Reputation: 747
I'm pretty new to angular, and I was thrust into a rather large project. I have a situation that I've simplified into the below code:
var beforeClose = function() {
var closeDeferred = $q.defer(),
a = $q.defer(),
b = $q.defer(),
c = $q.defer(),
promiseArray = [a.promise, b.promise, c.promise];
/* logic that resolves or rejects a, b, and c */
$q.all(promiseArray).then(
function() {
closeDeferred.resolve();
},
function() {
closeDeferred.reject();
}
);
return closeDeferred.promise;
}
var modalClose = function() {
beforeClose().then(
function() {
//close the modal
},
function() {
//warn and don't close modal
}
)
}
Using Chrome's DevTools it appears that the code is returning closeDeferred
promise before promiseArray
is completely resolved or rejected. So I guess my question is something like,
When is promise returned? Can it be returned before it has been resolved or rejected? If not, how can I prevent it from returning?
Putting the return statement into a function (say, in the promiseArray
resolve and reject functions) makes that function return the promise, but doesn't necessarily send that promise out to modalClose
(which fired the first promise).
Or maybe I understand what's going on with my code. When I've put the return closeDeferred
into a different function, I've gotten errors saying that .then()
can't be called on undefined (pointing at the beforeClose().then()
line).
Edit: After looking at Andrew's answer, I had to update the code, and also be more specific about what's happening.
The problem isn't necessarily that the promise gets returned before I want it, it's that it's executing the resolve function for that promise -- it closes the modal even though promiseArray()
hasn't finished doing its thing.
Edit 2: So it seems, in simplifying the code structure, I actually removed my real problem. In the various set ups for the promises it turns out that I had never actually defined closeDeferred as a defer or a promise before my return statement. Thus when the loop mechanism did what it's supposed to do, it returned closeDeferred.promise as well, undefined, I guess? so the modalClose function was running before any promises got resolved/rejected. Then on the next loop, closeDeferred was finally defined and the promises acted properly.
Still, I really appreciate the help that was given. both @andrew-tomlinson and @georgeawg gave me some much better understanding of promises. It didn't answer this problem (because I had defined the problem incorrectly) but it was definitely very helpful. Thanks!
Upvotes: 1
Views: 906
Reputation: 48968
Return the $q.all
promise.
var beforeClose = function() {
//var closeDeferred = $q.defer(),
var a = $q.defer(),
var b = $q.defer(),
var c = $q.defer(),
var promiseArray = [a.promise, b.promise, c.promise];
/* logic that resolves or rejects a, b, and c */
var pAll = $q.all(promiseArray).then(
function(resultsList) {
resultsA = resultsList[0];
resultsB = resultsList[1];
resultsC = resultsList[2];
return something;
},
function(error) {
throw error;
}
);
return pAll;
}
Be aware that $q.all
is not resilient. If any of the promises throws an error, it will resolve (as rejected) with the first error.
The $q.all
function will return a promise immediately. The promise will resolve (with data or error) sometime in the future. The $q
service delays execution of the function in the .then
method until the promise resolves with data; in the .catch
method with error.
var modalClose = function() {
//pAll gets set immediately with a promise
var pAll =
beforeClose()
.then( function(something) {
//waits for a,b,c to resolve
//close the modal
}) .catch( function (error) {
//executes with first error
//warn and don't close modal
});
}
Upvotes: 2
Reputation: 181
At first glance seems that you are loading the promiseArray with Defer objects. Based on the documentation (found here: https://docs.angularjs.org/api/ng/service/$q) it would seem that the all() method is expecting an array of Promise objects.
To get the promise from a Defer object you can access it as a property:
var myDeferedObj = $q.defer();
var myPromiseObj = myDeferedObj.promise;
Then you would use the promise objects in your promiseArray. This idea implemented in your example would alter it to look like:
a = $q.defer().promise,
b = $q.defer().promise,
c = $q.defer().promise,
promiseArray = [a,b,c];
Note, I have not tested this personally, but if you are still stuck, you can try it out. Good luck!
EDIT: After reading some of your questions more thoroughly, I feel like I need to alter my response to address some specifics. I'm sorry in advance if you already know this and I'm missing the point of the questions.
"When is the promise returned?"
A Promise object is returned from a function when the return
method executes. Returning a promise object is not conceptually the same as resolving or rejecting a promise. Returning a Promise object is usually done in order to pass the reference of the Promise around in an application. In your example, you pass a Promise object from the beforeClose()
function to the modalClose()
function. The callback functions provided on that Promise object (using .then(function(){},function(){})
) within modalClose()
will not execute until the promise is resolved or rejected.
"Can it be returned before it is resolved or rejected?"
Yes, the Promise object closeDeferred.promise
will be returned right away from beforeClose()
, but from my understanding of the example code it should not be resolved or rejected until the promises within the promiseArray are resolved or rejected collectively. That is to say, return closeDeferred.promise;
is not a blocking statement, and will execute just as any normal return statement at the end of a function.
There is a nifty (very simple) illustration of the Promise concept here explained as a cartoon.
Upvotes: 5
Reputation: 11499
I might be misreading this, but seems like you should send $q.all
promise objects and use the built in .catch
functionality of promises:
var beforeClose = function() {
var closeDeferred = $q.defer(),
a = $q.defer().promise, // send promise objects to promiseArray
b = $q.defer().promise, // send promise objects to promiseArray
c = $q.defer().promise, // send promise objects to promiseArray
promiseArray = [a,b,c];
/* logic that resolves or rejects a, b, and c */
$q.all(promiseArray)
.then(closeDeferred.resolve();)
.catch(closeDeferred.reject();)
return closeDeferred.promise;
}
var modalClose = function() {
beforeClose()
.then( //close the modal )
.catch( //warn and don't close modal );
}
Upvotes: 0