深入理解Angularjs 脏值检测(8)
我们再来定义$evalAsync,它添加将在这个队列上执行的函数:
Scope.prototype.$evalAsync = function(expr) { this.$$asyncQueue.push({scope: this, expression: expr}); };
我们显式在放入队列的对象上设置当前作用域,是为了使用作用域的继承,在这个系列的下一篇文章中,我们会讨论这个。
然后,我们在$digest中要做的第一件事就是从队列中取出每个东西,然后使用$eval来触发所有被延迟执行的函数:
Scope.prototype.$digest = function() { var ttl = 10; var dirty; do { while (this.$$asyncQueue.length) { var asyncTask = this.$$asyncQueue.shift(); this.$eval(asyncTask.expression); } dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { throw "10 digest iterations reached"; } } while (dirty); };
这个实现保证了:如果当作用域还是脏的,就想把一个函数延迟执行,那这个函数会在稍后执行,但还处于同一个digest中。
下面是关于如何使用$evalAsync的一个示例:
http://jsbin.com/ilepOwI/1/embed?js,console
作用域阶段
$evalAsync做的另外一件事情是:如果现在没有其他的$digest在运行的话,把给定的$digest延迟执行。这意味着,无论什么时候调用$evalAsync,可以确定要延迟执行的这个函数会“很快”被执行,而不是等到其他什么东西来触发一次digest。
需要有一种机制让$evalAsync来检测某个$digest是否已经在运行了,因为它不想影响到被列入计划将要执行的那个。为此,Angular的作用域实现了一种叫做阶段(phase)的东西,它就是作用域上一个简单的字符串属性,存储了现在正在做的信息。
在Scope的构造函数里,我们引入一个叫$$phase的字段,初始化为null:
function Scope() { this.$$watchers = []; this.$$asyncQueue = []; this.$$phase = null; }
然后,我们定义一些方法用于控制这个阶段变量:一个用于设置,一个用于清除,也加个额外的检测,以确保不会把已经激活状态的阶段再设置一次:
Scope.prototype.$beginPhase = function(phase) { if (this.$$phase) { throw this.$$phase + ' already in progress.'; } this.$$phase = phase; }; Scope.prototype.$clearPhase = function() { this.$$phase = null; };
在$digest方法里,我们来从外层循环设置阶段属性为“$digest”:
Scope.prototype.$digest = function() { var ttl = 10; var dirty; this.$beginPhase("$digest"); do { while (this.$$asyncQueue.length) { var asyncTask = this.$$asyncQueue.shift(); this.$eval(asyncTask.expression); } dirty = this.$$digestOnce(); if (dirty && !(ttl--)) { this.$clearPhase(); throw "10 digest iterations reached"; } } while (dirty); this.$clearPhase(); };
内容版权声明:除非注明,否则皆为本站原创文章。