0

Assume that we have a bit of html like that:

<button
  id="my-login-button"
  ng-hide="loggedIn"
  ng-click="login()">Log me in!</button>

Also we have a JavaScript:

// controller.js
$scope.login = function () {
    API.login()
      .then(function () {
        console.log('login promise resolved');
      });
};

// service.js
app.factory('API', ['$q', '$timeout', function ($q, $timeout) {
  return {
    login: function () {
      var login = $q.defer();

      // async login
      VK.Auth.login(
        function () {
          // login.resolve();       // doesn't work without $timeout()
          $timeout(login.resolve);  // works as it should
        },
        VK.access.FRIENDS | VK.access.AUDIO
      );

      return login.promise;
    }
  };
}]);

This piece of code works properly, but the mysterious part is near the $timeout() function. Why I should wrap my resolving with it? Why code doesn't work as expected without it?

I don't do something with scope variables, I'm just consoling. And without $timeout it will be called with next digest...

As for me it have no sense, if I need to change scope props, I will wrap everything in $apply.

Could anyone explain why usual deferred behavior became mysterious one?

P.S. I've solved my issue with $timeout after reading this question answers.

Community
  • 1
  • 1
ValeriiVasin
  • 8,628
  • 11
  • 58
  • 78

1 Answers1

2

In AngularJS promise results are propagated asynchronously, inside a $digest cycle. So, the results of then() are not propagated until the next digest cycle, which never comes without a $timeout or $http or $apply to trigger one.

See also Promise callback not called in Angular JS

Community
  • 1
  • 1
Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
  • I suppose that only $q behave like that. If we try to use $.Deferred() instead - it will not wait for next digest cycle... And it's strange... – ValeriiVasin May 29 '13 at 21:22