$evalAsync 用于延迟执行一段表达式。通常我们更习惯使用$timeout服务来进行代码的延迟执行,但$timeout会将执行控制权交给浏览器,如果浏览器同时还需要执行诸如 ui渲染/事件控制/ajax 等任务时,我们代码延迟执行的时机就会变得非常不可控。
我们来看看$evalAsync是如何让代码延迟执行的时机变得严格,可控的。
Scope.prototype.$evalAsync = function(expr) { var self = this; if(!self.$$phase && !self.$$asyncQueue.length) { setTimeout(function() { if(self.$$asyncQueue.length) { self.$root.$digest(); } }, 0); } this.$$asyncQueue.push({ scope: this, expression: expr }); };
$evalAsync方法的主要功能是从代码第11行开始,向$$asyncQueeu中添加对象。$$asyncQueue队列的执行是在$digest的do while循环中进行的。
while (this.$$asyncQueue.length) { try { var asyncTask = this.$$asyncQueue.shift(); asyncTask.scope.$eval(asyncTask.expression); } catch(e) { console.error(e); } }
$evalAsync的代码会在正在运行的$digest循环中被执行,如果当前没有正在运行的$digest循环,会自己延迟触发一个$digest循环来执行延迟代码。
$applyAsync
$applyAsync用于合并短时间内多次$digest循环,优化应用性能。
在日常开发工作中,常常会遇到要短时间内接收若干http响应,同时触发多次$digest循环的情况。使用$applyAsync可合并若干次$digest,优化性能。
/* 这个方法用于 知道需要在短时间内多次使用$apply的情况, 能够对短时间内多次$digest循环进行合并, 是针对$digest循环的优化策略 */ Scope.prototype.$applyAsync = function(expr) { var self = this; self.$$applyAsyncQueue.push(function() { self.$eval(expr); }); if(self.$root.$$applyAsyncId === null) { self.$root.$$applyAsyncId = setTimeout(function() { self.$apply(_.bind(self.$$flushApplyAsync, self)); }, 0); } };
$$postDigest
$$postDigest方法提供了在下一次digest循环后执行代码的方式,这个方法的前缀是"$$",是一个AngularJs内部方法,应用开发极少用到。
此方法不自主触发$digest循环,而是在别处产生$digest循环之后执行。
/* $$postDigest 用于在下一次digest循环后执行函数队列 不同于applyAsync 和 evalAsync, 它不触发digest循环 */ Scope.prototype.$$postDigest = function(fn) { this.$$postDigestQueue.push(fn); };
到这里,我们对脏检测的原理,即它的工作机制就了解的差不多了。希望这些知识能够帮助你更好的应用AngularJs来开发,能够更轻松地定位错误。
下一章,我会继续为大家介绍文章开头提到的另外两处scope相关的特性。篇幅较长,感谢您的耐心阅读~也希望大家多多支持脚本之家。
您可能感兴趣的文章: