我很久以前就看过这篇文章:
https://coderwall.com/p/ngisma
它描述了一种方法,如果我们不处于申请或摘要阶段,则触发$ apply。
$scope.safeApply = function(fn) {
var phase = this.$root.$$phase;
if(phase == '$apply' || phase == '$digest') {
if(fn && (typeof(fn) === 'function')) {
fn();
}
} else {
this.$apply(fn);
}
};
Angular有$scope.$evalAsync
方法(取自1.2.14):
$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});
}
如果我们不处于阶段,则调用digest并将当前调用添加到asyncQueue。
还有$ apply,$ digest和$ timeout方法。
令人困惑。
触发摘要周期(脏检查和数据绑定)所提到的所有方法之间有什么区别?
每种方法的用例是什么?
safeApply()还安全吗? :)
我们有什么替代方案而不是safeApply()(如果我们在摘要周期中调用$ apply)?
答案 0 :(得分:2)
作为一个高级别的介绍我会说很少需要实际启动你自己的摘要周期,因为角度处理大多数情况。
话虽如此,让我们深入研究这个问题。
作为一个非常高的级别,$ digest循环如下所示:
Do:
- - - If asyncQueue.length, flush asyncQueue.
- - - Trigger all $watch handlers.
- - - Check for "too many" $digest iterations.
While: ( Dirty data || asyncQueue.length )
所以基本上$evalAsync
正在将函数添加到asyncQueue
并在需要时推迟摘要。但是,如果它已经处于摘要周期,它将刷新asyncQueue
,它只会调用该函数。
您可能会注意到非常与safeApply
类似。一个不同之处在于,不是将函数添加到asyncQueue
,而是仅调用它,这可能发生在例如循环的中间。另一个区别是它暴露并依赖于$$
变量,它们是内部的。
$evalAsync
$apply
与$digest
之间最重要的差异(我将在下面$timeout
)$evalAsync
和{{1} }在$apply
处启动摘要,但您在任何范围内调用$rootScope
。如果您认为需要这种灵活性,则需要评估您的个案。
使用$digest
非常类似于$timeout
,除非它总是将其推迟到当前摘要周期(如果有的话)。
$evalAsync
如果你已经在摘要周期中给你
$timeout(function() {
console.log( "$timeout 1" );
});
$scope.$evalAsync(function( $scope ) {
console.log( "$evalAsync" );
});
即使它们以相反的顺序被调用,因为超时一被延迟到它实例化的下一个摘要周期。
编辑对于评论中的问题。
据我所知,$evalAsync
$timeout 1
和$apply
之间的最大区别是$evalAsync
实际上是在触发$apply
周期。对您而言,这意味着您需要确保在未进入摘要周期时致电$digest
。只是为了透明,以下代码也是有效的:
$apply
将调用apply函数,将no-op函数添加到$scope.$apply(function() {
$scope.$evalAsync(function() {
});
});
并开始摘要循环。文档说,建议每当模型发生变化时都使用$asyncQueue
,这可能发生在$apply
$evalAsync
和fn(); $scope.apply()
之间的区别仅在于$scope.apply(fn)
为函数执行try catch并明确使用$scope.apply(fn)
。此外,您实际上可以尝试在$exceptionHandler
中导致摘要周期,这将改变处理申请的方式。
我还想在这个时候指出1.3中的更复杂,假设它保持这种状态。将有一个名为fn
的函数用于推迟应用调用(reference)。
这篇文章汇编了This blog和so post的一些信息以及我的一些经验。
希望这有帮助!
答案 1 :(得分:1)
只有一种方法可以启动摘要周期:调用$digest
。它很少直接调用。
$apply
本质上是$digest
的安全包装器。安全意味着它处理错误。因此,大多数情况下,这是您要调用以传播更改的方法。另一个重要的区别是它总是在根范围内开始一个摘要周期,而$digest
可以在任何范围内调用,只影响该范围及其后代。
许多服务和指令通常通过调用$apply
来触发摘要周期。唯一需要知道的是,你必须自己调用$apply
或者已经被service / directive调用了。
safeApply
仍然安全吗?
是的,因为仍然只有一种方法可以启动摘要周期。
我们有什么替代方案而不是safeApply()(如果我们在摘要周期中调用$ apply)?
在摘要周期中不调用$apply
。认真。处于这种情况几乎总是一个设计问题。最好解决这个问题,而不是覆盖它。
无论如何,解决方案使用的方法,检查$$phase
,是了解摘要周期是否正在运行的唯一方法。如你所示,它甚至在内部被Angular使用。做if (!$rootScope.$$phase)
就足够了。但请记住,这可能会破坏新版本的Angular。顺便说一句:正如作者提出的那样,修补最上面的范围会使解决方案对孤立的范围无用。
对于刚开始使用Angular的人来说,Angular的数据绑定方式确实很难理解。但基本上所有这一切归结为:对于要识别/处理的每个更改,有人必须致电$apply
(或$digest
。而已。这应该由指令和服务完成。如果你做的一切正确的话,实际上很难得到'$ apply in progress'错误。