$q service在angularjs中非常强大,使我们的异步代码生活更轻松。
我是角色的新手,但使用延迟API对我来说并不是什么新鲜事。我必须说我完全没有文档的How to use
部分+文档中有非常有用的链接+我检查了源代码。
我的问题更多的是关于引擎盖部分的延迟和承诺API对象的角度。 生命周期中的确切阶段是什么?它们如何与rootScope.Scope
(s)进行交互。我的假设是,当promise解决时 - 它会调用摘要循环???是/否?
可以针对以下方面列表提供详细的答案:
我会欣赏并接受最详细的答案,尽可能多地引用文档或来源(我自己无法找到)。我无法找到之前有关此主题的讨论,如果已经有 - 请发布链接。
ps:对于通过为此问题建议更好标题而有所帮助的+1,请在评论中添加您的建议。
干杯!
答案 0 :(得分:24)
承诺有三个州
.then
的返回值达到时所发生的情况,并且通常类似于标准返回值。throw
处理程序.then
或当您返回解除拒绝承诺*的承诺时,会发生这种情况,它通常类似于抛出的标准异常。在Angular中,promises异步解析并通过$rootScope.$evalAsync(callback);
(取自here)解析来提供保证。
由于它是通过$evalAsync
运行的,因此我们知道在promise(通常)解析后至少会发生一个摘要周期,因为如果没有进行摘要,它将安排一个新的摘要。
这也是为什么例如当你想在Angular中单元测试promise代码时,你需要运行一个摘要循环(通常在rootScope
上通过$rootScope.digest()
),因为$ evalAsync执行是摘要循环。
注意:这显示了来自Angular 1.2的代码路径,Angular 1.x中的代码路径都相似但是在1.3+ $ q中已经重构为使用原型继承所以这个答案不是这些版本的代码准确(但在精神上)。
1)当创建$ q时,它会this:
this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
return qFactory(function(callback) {
$rootScope.$evalAsync(callback);
}, $exceptionHandler);
}];
反过来,确实:
function qFactory(nextTick, exceptionHandler) {
仅在nextTick
内作为$evalAsync
传递给解析并通知:
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
。
反过来在$ digest中(在一个周期内,在测试观察者之前):
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;
});
完美,保持简单。
* note angular将抛出与拒绝区分开来 - 默认情况下会记录抛出并且必须明确记录拒绝
答案 1 :(得分:8)
当promise解析时 - 它会调用摘要循环吗?
是。您可以通过一个简单的模板进行测试:
{{state}}
和在延迟后更改$scope.state
变量的控制器代码:
$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
调用其回调,这是一个将回调传递给$rootScope.$evalAsync
的函数,根据$evalAsync
docs }:
表达式执行后将执行至少一个$摘要周期
注意:如果在$ digest周期之外调用此函数,将安排新的$ digest周期
所以堆栈是否已经在$ digest循环中可以改变事件的确切顺序。
&#39; 1。在您描述的每个步骤/阶段中发生的事情的顺序是什么
详细介绍上一个例子:
var d = $q.defer();
延迟对象的承诺处于暂挂状态。此时,几乎没有任何事情发生,您只有一个具有resolve
,reject
,notifiy
和promise
属性的延迟对象。没有使用或影响$ digest循环
d.promise.then(function() {
$scope.state = 'Resolved, and digest cycle must have run';
});
承诺仍处于暂挂状态,但注册了then
次成功回调。同样,没有使用或影响$ digest循环或任何范围。
$window.setTimeout(function() {
d.resolve();
}, 1000);
1秒后,将调用d.resolve
。这会将上面步骤2中定义的回调传递给$evalAsync (via nextTick)。
$ evalAsync将调用回调
$ evalAsync将确保调用一个$ digest周期。
&#39; 2。当新的deferred对象创建了一个新的promise实例时 - 谁知道它/它是否重要?
仅限$q.defer()
的来电者。在resolved
被调用(或者确实是reject
或notify
)之前,任何范围都没有任何变化。
&#39; 3。范围更新的承诺对象是如何解析的?我是否必须在回调中手动更新它,否则将自动调用摘要并更新rootScope,就像在此声明一样
如上所述,$ digest循环将通过调用resolve
自动启动(如果它已经不在其中)。
&#39; 4。提到至少一种从promise回调中更新范围的方法
上面的例子给出了这个。
&#39; 5。我假设有很多其他有用的方面,随时提供它们。
不是我能想到的!