chaining $http calls?

2,988 views
Skip to first unread message

coli

unread,
May 4, 2012, 12:50:27 PM5/4/12
to ang...@googlegroups.com
Trying to get my head around how to chain two (or more)$http calls in serial? (without nested callbacks)

Something like?

$http().then(
    $http() ?
).then(
    $http() ?
)
Message has been deleted
Message has been deleted

coli

unread,
May 4, 2012, 1:24:58 PM5/4/12
to ang...@googlegroups.com
This appears to work

$http().then(function(){
    return $http();
}.then(function(){
    return $http();
}.then(function(){
    //done
});


Doesn't work. the alert("done") was never called. Anyone know why?

coli

unread,
May 4, 2012, 1:33:59 PM5/4/12
to ang...@googlegroups.com
Interesting, I didn't realize you have to call $scope.$apply(), after promise.resolve() 

The example does have the apply call, but it should probably be called out that it is needed even if you did not change the scope model data.

Peter Bacon Darwin

unread,
May 5, 2012, 2:00:45 AM5/5/12
to ang...@googlegroups.com
That is strange.  From looking at the source, I don't see why the $apply is needed. I wonder why this is?

--
You received this message because you are subscribed to the Google Groups "AngularJS" group.
To view this discussion on the web visit https://groups.google.com/d/msg/angular/-/4RZaiBHamKIJ.

To post to this group, send email to ang...@googlegroups.com.
To unsubscribe from this group, send email to angular+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/angular?hl=en.

ThomasBurleson

unread,
May 5, 2012, 10:14:00 AM5/5/12
to ang...@googlegroups.com
Best not to hook into window.setTimeout(). 
Instead use AngularJS $defer() to pause/delay a function call.

Also, you should wait after $scope changes before you call alerts.

Peter Bacon Darwin

unread,
May 5, 2012, 11:59:02 AM5/5/12
to ang...@googlegroups.com
It is true that $defer is better for delays than setTimeout when in an angular app but I suspect that coli was just using it to simulate an asynch call to something outside angular in which case there would be no setTimeout but it appears that we still need the $apply.
By the way in your fiddle Thomas you put that you are delaying for 100ms to allow the scope change to come into affect.  This is not how angular works.  There is no other thread that will kick in and notice the scope change if you just wait for a bit.  What is actually happening is that by calling $defer(...,300) you are actually triggering a $apply, which in turn triggers a $digest which catches the scope changes.  This can be done simply with a $apply if you know you are outside angular and you don't need a delay.  If you are not sure if you are in an angular $apply call already then you can use $defer without a time-out value, which effectively triggers a new $apply loop immediately after this call has completed.
Pete

--
You received this message because you are subscribed to the Google Groups "AngularJS" group.
To view this discussion on the web visit https://groups.google.com/d/msg/angular/-/-AosPj3-Y0EJ.

Suller Andras

unread,
May 5, 2012, 12:13:17 PM5/5/12
to ang...@googlegroups.com
On Sat, May 5, 2012 at 10:59 PM, Peter Bacon Darwin
<pe...@bacondarwin.com> wrote:
> By the way in your fiddle Thomas you put that you are delaying for 100ms to
> allow the scope change to come into affect.  This is not how angular works.
>  There is no other thread that will kick in and notice the scope change if
> you just wait for a bit.  What is actually happening is that by calling

I guess he meant that if you do not use $defer for alert, you will see
the old value on the screen while the alert is visible, and it will
update the screen only after you closed the alert box.

Andras

Peter Bacon Darwin

unread,
May 5, 2012, 1:16:15 PM5/5/12
to ang...@googlegroups.com

Oh yeah. Sorry

...from my mobile.

--
You received this message because you are subscribed to the Google Groups "AngularJS" group.

ThomasBurleson

unread,
May 6, 2012, 4:20:14 PM5/6/12
to ang...@googlegroups.com
@Andras,
Exactly. Sorry I did not clearly articulate why the $defer() was used [in my previous email].

ThomasBurleson

unread,
May 6, 2012, 5:03:43 PM5/6/12
to ang...@googlegroups.com
@Peter,
I knew that if I used setTimeout() I would have to wrap the $scope.name assignment in a $scope.$apply() call.

var myApp       = angular.module('myApp',[]),
   updateName  = null;


function MyCtrl($scope, $defer, $q) {
   
        updateName = function () {
           $scope.$apply(function() {
               $scope.name = "... Goodbye SuperHero [ using $apply() ]";
           });            
       };            
   
       window.setTimeout("updateName()",500);
   
   $scope.name = 'Hello Superhero';
   
}​

I did not realize that $defer() implicit calls $apply() as a wrapper around the deferred function/callback. 


function MyCtrl($scope, $defer, $q) {
   
   $defer( function() {
       $scope.name = "Goodbye SuperHero [ using $defer() ]";
   }, 1000);
   
   $scope.name = 'Hello Superhero';
   
}​


Thanks for giving me more insight into how $defer participates in the $apply() `dirty` process.
That is COOL. 

- Thomasb


jupit...@gmail.com

unread,
Jul 11, 2012, 10:47:33 AM7/11/12
to ang...@googlegroups.com
Hello,

I have looked into the code and I think I found the reason, why AngluarJS promises are resolved only once we call $apply. The q factory receives a tick function as first parameter. The  resolution of the promise is wrapped inside this tick function. The p Provider passes uses $evalAsync as the tick function so that the promise is resolved ansynchronously sometime in the current execution context of the root scope before the $digest cycle. In fact the $digest method calls all expressions added to the $$asyncQueue by the $evalAsync method. That's why the deferred only get's resolved once we call $apply which in turn calls $digest.

The problem is that this produces quite an overhead if you chain multiple thens together as in the following example:

// database is ready
dbInit.promise.then(function() {
   Object.get(id, function(object) {
      $rootScope.$apply(deferred.resolve(id));
   }
  return deferred.promise;
// retrieve the relationship
}).then(function(object) {
  Relationship.get(object.relationship), function(relationship) {
     $rootScope.$apply(deferred.resolve(relationship))
  }
}).then(function(relationship) {
  success(relationship)
}

This is quite an overhead especially if I travers an object hierarchy and must fetch numerous objects asynchronously. Each time I must call $apply and run through the whole process of dirty checking. Is there any way around this?
To unsubscribe from this group, send email to angular+unsubscribe@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages