4

I have the following code:

someService.fnReturnsPromise()
    .then(function () {
        return someService.fnReturnsAnotherPromise(someArg);
    })
    .then(function (resultsOfSecondFn) {
        // do stuff with results
    });

I feel as if this should work; however, resultsOfSecondFn isn't actually the results, it's the promise itself that I returned. To make it work the way I want, I have to do this:

someService.fnReturnsPromise()
    .then(function () {
        return someService.fnReturnsAnotherPromise(someArg);
    })
    .then(function (promiseReturn) {
        promiseReturn.then(function (results) {
            // do stuff with results
        });
    });

This is the pseudo-code for fnReturnsAnotherPromise:

someService.fnReturnsAnotherPromise = function (arg1) {
    return anotherService.anotherFnThatReturnsPromise(arg1);
};

So really, it's just one extra layer, but a promise is getting returned either way. The code for anotherFnThatReturnsPromise is the simple paradigm of $q.defer(), return dfd.promise with some resolve()s.

John Slegers
  • 45,213
  • 22
  • 199
  • 169
user2857125
  • 196
  • 2
  • 11
  • If `someService.fnReturnsAnotherPromise(someArg);` would not resolve with a promise, your first example would be perfect. Please share actual code so I can help you – apairet Aug 26 '14 at 17:46
  • How does your service look like? It should work.. http://plnkr.co/edit/aASPzN Are you using $http promise directly from the service? – PSL Aug 26 '14 at 17:48
  • I've updated the OP for some clarification. It calls another function that returns a promise. I don't think that should cause a problem. – user2857125 Aug 26 '14 at 17:58
  • @user2857125 see my answer - promises in Angular will recursively assimilate. You can nest them arbitrarily and they'll resolve correctly. – Benjamin Gruenbaum Aug 26 '14 at 18:00

2 Answers2

6

Promises like Angular's are Promises/A+ compliant and are guaranteed to recursively assimilate promises. This is exactly to avoid nesting and simplify things like your case and is the point of promises.

So even if you have a promise that returns and a promise that returns a promise you can unwrap it in a single .then call. For example:

var p  = $q.when(1); // Promise<Int>
var p2 = $q.when().then(function(){ return p;  }); // Promise<Promise<Int>>
var p3 = $q.when().then(function(){ return p2; }); // Promise<Promise<Promise<Int>>>>
p3.then(function(result) {
    console.log(result); // Logs 1, and Int and not p2's type
});

Or in your example:

someService.fnReturnsPromise()
.then(function() {
    return someService.fnReturnsAnotherPromise(someArg);
})
.then(function(resultsOfSecondFn) {
    // do work with results, it is already unwrapped
});

See this comparison with another language for perspective on promises not unwrapping.

Community
  • 1
  • 1
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • Unfortunately, when I inspect `resultsOfSecondFn`, it is a promise object. There is one extra layer of complexity in how I call it (I've updated the OP), but I don't think that should cause any issue? – user2857125 Aug 26 '14 at 18:01
  • @user2857125 you can nest it in 250 levels of promise, it will still resolve correctly and that fact is both battle tested as well a tested against in the unit tests of Angular - you can rely on it. – Benjamin Gruenbaum Aug 26 '14 at 18:02
  • Question for you: Do jQuery promises unwrap themselves in the same manner? – user2857125 Aug 26 '14 at 18:16
  • 1
    @user2857125 that's an interesting question - read [Problems inherent to jQuery's Deferred](http://stackoverflow.com/questions/23744612/problems-inherent-to-jquery-deferred) and [You're missing the point of promises!](http://domenic.me/2012/10/14/youre-missing-the-point-of-promises/). Generally - yes after jQuery 1.8, no before (unless you used pipe instead of `.then`). – Benjamin Gruenbaum Aug 26 '14 at 18:19
  • Well, that was the issue. So, `fnReturnsPromise` was actually returning a jQuery Promise. This library is also, apparently, including jQuery 1.11.1. As a result, I'm chaining off of a jQuery promise and not an Angular promise. Feel free to update your answer and I'll mark it. – user2857125 Aug 26 '14 at 18:23
  • @user2857125 if the jQuery promise is of version 1.11.1 it should still work - so no problem :) – Benjamin Gruenbaum Aug 26 '14 at 18:25
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/60033/discussion-between-user2857125-and-benjamin-gruenbaum). – user2857125 Aug 26 '14 at 18:27
-2

you could try it this way:

someService.fnReturnsPromise().then(function () {
    someService.fnReturnsAnotherPromise(someArg).then(function (results) {
        console.log(results);
    });
});

Hope it helps!

lucho99
  • 172
  • 3
  • 2
    Hi lucho, I didn't downvote and I consider those that do, without commenting, are a scourge on stack. Just fyi: the reason you did get downvoted was because nesting promises is generally considered bad practice and can leave you worse off than with multiple promise chains. – GFoley83 Jan 04 '15 at 19:07