AngularJS:$ q – >事物的延期API顺序(生命周期)和谁调用摘要?

$ q服务在angularjs中非常强大,并且使asynchronous代码更轻松。

我是新angular度,但使用延期的API对我来说并不是很新鲜。 我必须说,我完全确定How to use文档的一部分+有非常有用的链接,在文档+我检出了源。

我的问题是更多关于延迟和承诺的API对象的angular度下的引擎盖部分。 它们生命周期中的确切阶段是什么?它们如何与rootScope.Scope (s)交互。 我的假设是,当承诺解决 – 它调用摘要循环? 是/否?

能否详细回答以下几个方面的具体问题:

  1. 每个描述的步骤/阶段发生的事情的顺序是什么?
  2. 当新的延迟对象与一个新的承诺实例创build – 谁知道它/是否重要?
  3. 承诺对象解决时,范围如何更新? 是否必须在callback中手动更新它,否则将自动调用摘要并更新rootScope, 如同在此处声明的那样
  4. 提及至less一种从promisecallback中更新范围的方法
  5. 我假设有很多其他有用的方面,随时提供他们。

我会欣赏并接受最详细的答案,尽可能多地引用文档或来源(我自己找不到)。 我找不到任何以前的讨论这个话题,如果已经有 – 请张贴链接。

ps:对任何一个有帮助的人都可以通过为这个问题build议一个更好的标题,请在评论中添加您的build议。

干杯!

承诺有三个州

  • 待定 – 这是承诺如何开始。
  • 实现 – 这是当你解决延期,或者当时的返回值满足时发生的情况,它通常类似于标准的返回值。
  • 被拒绝 – 当你从一个.then处理程序中throw一个延迟,或者当你返回一个拒绝拒绝*的承诺时,会发生这种情况,这通常类似于抛出的一个标准exception。

在Angular中,promise通过$rootScope.$evalAsync(callback);来parsing,并通过parsing来提供保证$rootScope.$evalAsync(callback); (从这里拿)。

由于它是通过$evalAsync运行的,我们知道在承诺parsing(通常)之后至less会有一个摘要循环,因为如果没有进行,它将安排一个新的摘要。

这也是为什么,例如当你想在Angular中unit testing承诺代码时,你需要运行一个digest循环(通常在rootScope通过$rootScope.digest() ),因为执行$ evalAsync是摘要循环的一部分。

好吧,说够了,给我看代码:

注意:这显示了Angular 1.2中的代码path,Angular 1.x中的代码path都是相似的,但是在1.3+ $ q中被重构为使用原型inheritance,所以这个答案在代码中是不准确的(但在精神上)这些版本。

1)当$ q被创build时,它会这样做:

  this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) { return qFactory(function(callback) { $rootScope.$evalAsync(callback); }, $exceptionHandler); }]; 

而这反过来呢:

 function qFactory(nextTick, exceptionHandler) { 

并且只parsing在nextTick作为$evalAsync nextTick传递的内部parsing并通知:

  resolve: function(val) { if (pending) { var callbacks = pending; pending = undefined; value = ref(val); if (callbacks.length) { nextTick(function() { var callback; for (var i = 0, ii = callbacks.length; i < ii; i++) { callback = callbacks[i]; value.then(callback[0], callback[1], callback[2]); } }); } } }, 

在根作用域上,$ evalAsync被定义为:

  $evalAsync: function(expr) { // if we are outside of an $digest loop and this is the first time we are scheduling async // task also schedule async auto-flush if (!$rootScope.$$phase && !$rootScope.$$asyncQueue.length) { $browser.defer(function() { if ($rootScope.$$asyncQueue.length) { $rootScope.$digest(); } }); } this.$$asyncQueue.push({scope: this, expression: expr}); }, $$postDigest : function(fn) { this.$$postDigestQueue.push(fn); }, 

如你所见,如果我们不在一个,而且以前没有计划过摘要,那么你可以看到确实安排了一个摘要。 然后它将函数推到$$asyncQueue

在$摘要内(在一个周期内, testing观察者之前 ):

  asyncQueue = this.$$asyncQueue, ... while(asyncQueue.length) { try { asyncTask = asyncQueue.shift(); asyncTask.scope.$eval(asyncTask.expression); } catch (e) { clearPhase(); $exceptionHandler(e); } lastDirtyWatch = null; } 

所以,正如我们所看到的,它运行在$$asyncQueue直到它是空的,在您的承诺中执行代码。

所以,我们可以看到,更新范围仅仅是分配给它,如果它尚未运行,摘要将运行,如果是,运行在观察者运行之前调用在$evalAsync上运行的promise中的代码。 所以一个简单的:

 myPromise().then(function(result){ $scope.someName = result; }); 

简单,简单。

*注意angular度区分抛出抛出 – 默认情况下抛出抛出,拒绝必须明确logging

当承诺解决 – 它调用摘要循环?

是。 你可以用一个简单的模板来testing它:

 {{state}} 

以及在延迟后更改$scope.statevariables的控制器代码:

 $scope.state = 'Pending'; var d = $q.defer(); d.promise.then(function() { $scope.state = 'Resolved, and digest cycle must have run'; }); $window.setTimeout(function() { d.resolve(); }, 1000); 

你可以在http://plnkr.co/edit/fIfHYz9EYK14A5OS6NLd?p=preview看到。; 一秒之后,HTML中的文本显示为Resolved, and digest cycle must have run 。 调用setTimeout而不是$timeout是故意的,以确保它必须被resolve ,最终启动摘要循环。

这可以通过查看源代码来确认: resolve通过nextTick调用其callbacknextTick ,这是一个将callback函数传递给$rootScope.$evalAsync的函数$rootScope.$evalAsync ,根据$evalAsync文档 :

执行expression式之后至less要执行一个$ digest循环

那些同样的文档也说:

注意:如果这个函数是在$ digest循环之外被调用,那么将会计划一个新的$ digest循环

所以堆栈是否已经在$ digest循环中可以改变事件的确切顺序。


“1。 每个描述的步骤/阶段发生的事情的顺序是什么?

详细介绍前面的例子:

  1. var d = $q.defer(); 延期对象的承诺处于未决状态。 在这一点上,几乎没有任何事情发生,你只是有一个延期对象的resolverejectnotifiypromise属性。 没有使用或影响$摘要循环

  2. d.promise.then(function() { $scope.state = 'Resolved, and digest cycle must have run'; });

    该承诺仍然处于未决状态,但已成功callback注册。 同样,没有使用或影响$摘要循环或任何范围。

  3. $window.setTimeout(function() { d.resolve(); }, 1000);

    1秒后, d.resolve将被调用。 这将上面步骤2中定义的callback传递给$ evalAsync(通过nextTick) 。

  4. $ evalAsync将调用callback

  5. $ evalAsync将确保一个$ digest循环被调用。


“2。 当新的延迟对象与一个新的承诺实例创build – 谁知道它/是否重要?

只有$q.defer()的调用者。 在调用resolved之前(或确实rejectnotify ),任何范围都不会发生任何变化。


“3。 如何更新范围更新诺言对象被解决? 是否必须在callback中手动更新它,否则将自动调用摘要并更新rootScope,如同在此处声明的那样

如前所述,$ digest循环将通过调用resolve来自动启动(如果它不在其中)。


“4。 提及至less一种从promisecallback中更新范围的方法

上面的例子给出了这个。


“5。 我假设有很多其他有用的方面,随时提供他们。

不是我能想到的!