32

I am trying to chain together multiple deferred function calls such that the next call gets the results of the previous deferred.resolve. When I chain together more than 2 of these calls, the data stops being returned.

Here is the basic code inside an angular controller:

    $scope.runAsync = function() {
        var asyncFn1 = function(data){
            var deferred = $q.defer();

            $timeout(function(){
                console.log("Async fn1 " + data);
                $scope.outputLines.push("Async fn1 " + data);
                deferred.resolve("Async fn1 " + data);
            },1000);

            return deferred.promise;
        }

        var asyncFn2 = function(data){
            var deferred = $q.defer();

            $timeout(function(){
                console.log("Async fn2 " + data);
                $scope.outputLines.push("Async fn2 " + data);
                deferred.resolve("Async fn2 " + data);
            },1000);

            return deferred.promise;
        }

        asyncFn1(1)
        .then(function(data){asyncFn2(data)})
        .then(function(data){asyncFn2(data)})
        .then(function(data){asyncFn2(data)});
    }

When I run this I get the following output:

Async fn1 1
Async fn2 Async fn1 1
Async fn2 undefined
Async fn2 undefined

How can I chain them together so that the third call gets the result from the second call and the fourth gets the result from the third?

I have created a jsFiddle: http://jsfiddle.net/rhDyL/

BoxerBucks
  • 3,124
  • 2
  • 21
  • 26

1 Answers1

40

Excerpt taken from the official doc on $q:

then(successCallback, errorCallback) – regardless of when the promise was or will be resolved or rejected calls one of the success or error callbacks asynchronously as soon as the result is available. The callbacks are called with a single argument the result or rejection reason.

This method returns a new promise which is resolved or rejected via the return value of the successCallback or errorCallback.

And for the return value of the successCallack or errorCallback, according to Domenic's slides:

if the return value is a promise then the promise adopts the returned promise's state otherwise the success callback is immediately called with the return value

Based on the definition, your code is missing the return keyword. It should be as following:

    asyncFn1(1)
    .then(function(data){return asyncFn2(data)})
    .then(function(data){return asyncFn2(data)})
    .then(function(data){return asyncFn2(data)});
tamakisquare
  • 16,659
  • 26
  • 88
  • 129
  • Wow.. thanks. I kind of see it now. I'm just confused by the fact that the .then() method returns a new promise, but inside the asyncFn I am returning a promise from the deferred object. It seems like double the number of promises. – BoxerBucks Apr 18 '13 at 03:33
  • 2
    I was in the same position myself not long ago. _Promise & deferred_ does require a bit of learning curve and practice to get used to. Nevertheless, I think it's worth it. It makes managing code execution in the world of async programming much more easier. If you are still not familiar with the topic, I highly recommend [this reading](http://www.slideshare.net/domenicdenicola/promises-promises). – tamakisquare Apr 18 '13 at 05:45
  • 1
    @tamakisquare - thanks for the response and comment. I think the key point of confusion which is clarified in slide #7 of [domenic's presentation](http://www.slideshare.net/domenicdenicola/promises-promises) is that if the return value is a promise then the promise adopts the returned promise's state otherwise the success callback is immediately called with the return value. Maybe you can modify your answer to reflect that also - to be more complete. Thanks. – Amir Oct 17 '13 at 07:59
  • @Amir - Thanks for the suggestion. I'll shall add that to the answer. FYI, You'll earn the privilege to refine/modify others' answers once you reach 2000 points. – tamakisquare Oct 20 '13 at 19:38
  • @tamakisquare - great. Thank you. And I'll work towards getting the 2k points. :) – Amir Oct 21 '13 at 21:15