AngularJS:$ q – >事物的延期API顺序(生命周期)和谁调用摘要?
$ q服务在angularjs中非常强大,并且使asynchronous代码更轻松。
我是新angular度,但使用延期的API对我来说并不是很新鲜。 我必须说,我完全确定How to use
文档的一部分+有非常有用的链接,在文档+我检出了源。
我的问题是更多关于延迟和承诺的API对象的angular度下的引擎盖部分。 它们生命周期中的确切阶段是什么?它们如何与rootScope.Scope
(s)交互。 我的假设是,当承诺解决 – 它调用摘要循环? 是/否?
能否详细回答以下几个方面的具体问题:
- 每个描述的步骤/阶段发生的事情的顺序是什么?
- 当新的延迟对象与一个新的承诺实例创build – 谁知道它/是否重要?
- 承诺对象解决时,范围如何更新? 是否必须在callback中手动更新它,否则将自动调用摘要并更新rootScope, 如同在此处声明的那样
- 提及至less一种从promisecallback中更新范围的方法
- 我假设有很多其他有用的方面,随时提供他们。
我会欣赏并接受最详细的答案,尽可能多地引用文档或来源(我自己找不到)。 我找不到任何以前的讨论这个话题,如果已经有 – 请张贴链接。
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.state
variables的控制器代码:
$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。 每个描述的步骤/阶段发生的事情的顺序是什么?
详细介绍前面的例子:
-
var d = $q.defer();
延期对象的承诺处于未决状态。 在这一点上,几乎没有任何事情发生,你只是有一个延期对象的resolve
,reject
,notifiy
和promise
属性。 没有使用或影响$摘要循环 -
d.promise.then(function() { $scope.state = 'Resolved, and digest cycle must have run'; });
该承诺仍然处于未决状态,但已成功callback注册。 同样,没有使用或影响$摘要循环或任何范围。
-
$window.setTimeout(function() { d.resolve(); }, 1000);
1秒后,
d.resolve
将被调用。 这将上面步骤2中定义的callback传递给$ evalAsync(通过nextTick) 。 -
$ evalAsync将调用callback
-
$ evalAsync将确保一个$ digest循环被调用。
“2。 当新的延迟对象与一个新的承诺实例创build – 谁知道它/是否重要?
只有$q.defer()
的调用者。 在调用resolved
之前(或确实reject
或notify
),任何范围都不会发生任何变化。
“3。 如何更新范围更新诺言对象被解决? 是否必须在callback中手动更新它,否则将自动调用摘要并更新rootScope,如同在此处声明的那样
如前所述,$ digest循环将通过调用resolve
来自动启动(如果它不在其中)。
“4。 提及至less一种从promisecallback中更新范围的方法
上面的例子给出了这个。
“5。 我假设有很多其他有用的方面,随时提供他们。
不是我能想到的!