为什么$ scope.apply对angular的$ q很重要?

时间:2014-05-09 08:55:51

标签: angularjs

我正在关注AngularJS关于Q实现$ q的文档的帮助。我尝试了https://docs.angularjs.org/api/ng/service/$q

中的以下代码
// for the purpose of this example let's assume that variables `$q`, `scope` and `okToGreet`
  // are available in the current lexical scope (they could have been injected or passed in).

  function asyncGreet(name) {
    var deferred = $q.defer();

    setTimeout(function() {
      // since this fn executes async in a future turn of the event loop, we need to wrap
      // our code into an $apply call so that the model changes are properly observed.
      scope.$apply(function() {
        deferred.notify('About to greet ' + name + '.');

        if (okToGreet(name)) {
          deferred.resolve('Hello, ' + name + '!');
        } else {
          deferred.reject('Greeting ' + name + ' is not allowed.');
        }
      });
    }, 1000);

    return deferred.promise;
  }

  var promise = asyncGreet('Robin Hood');
  promise.then(function(greeting) {
    alert('Success: ' + greeting);
  }, function(reason) {
    alert('Failed: ' + reason);
  }, function(update) {
    alert('Got notification: ' + update);
  });

我的理解是这里的$ scope.apply是给回调Angular的上下文,并确保可以访问$ scope下的变量。

但是在比较$ q和Kris Kowal的Q时,测试代码如下:

it('should simulate promise', inject(function($q, $rootScope) {
   var deferred = $q.defer();
   var promise = deferred.promise;
   var resolvedValue;

   promise.then(function(value) { resolvedValue = value; });
   expect(resolvedValue).toBeUndefined();

   // Simulate resolving of promise
   deferred.resolve(123);

   // Note that the 'then' function does not get called synchronously.
   // This is because we want the promise API to always be async, whether or not
   // it got called synchronously or asynchronously. 
   expect(resolvedValue).toBeUndefined(); // <= so the deferred is not resolved without the 'apply'?

   // Propagate promise resolution to 'then' functions using $apply().
   $rootScope.$apply();
   expect(resolvedValue).toEqual(123);
 }));

它说$ rootScope。$ apply()是将promise解析传播到'then'。我很困惑...所以不使用apply,deferred.resolve实际上不会解决?

1 个答案:

答案 0 :(得分:9)

这个文档很糟糕。

所以$q的问题是当你的promise被解决时(也可能是拒绝或通知),它会在$rootScope.evalAsync内调用你的处理程序,这确保了在调用之后,它会触发一个摘要,因此您的应用程序的其余部分可以有机会更新或以其他方式响应更改,就像我们喜欢的方式。

正如您所发现的,it works just fine without the explicit $apply in an example app。但是,他们明确$apply的原因是因为$evalAsync的自动化在测试中同步运行时没有机会运行,而不是因为您的应用程序需要它Just Work™

使用其他一些值得在angular-mock.js进行测试的显着服务,例如$http$timeout,我们可以在我们想要模拟异步http请求/响应时显式刷新超时(例如)。相当于等待eval ed的东西是触发摘要,它将在适当的上下文中调用您的promise处理程序。这是通过$apply$digest完成的,因此您可以在他们的示例中看到它...... 因为他们的示例是作为同步测试编写的

文档应该解释您需要做什么才能使测试工作与应用程序本身应该关注什么才能完成工作之间的区别。文档有一个习惯,就是以测试事实为例,这只会让人感到困惑。

希望有所帮助。