初探angular的zonjs
在angular中有个神奇的东西,就是zonejs,它包含了angular的脏检测,异步追踪,等各种高大上的东西,那么zonjs究竟是什么玩意呢?
- Zone定义
它描述JavaScript执行环境,可以在异步任务之间进行持久性传递,它类似于Java中的TLS(thread-local storage: 线程本地存储)技术,zone.js则是将TLS引入到JavaScript语言中的实现框架。
- 引入问题
在js的世界里,所有代码是单线程执行的,如果没有异步代码,检测性能什么的都没什么问题,如下:
var foo = function(){ ... }, bar = function(){ ... }, baz = function(){ ... }; foo(); bar(); baz();
这段代码很简单,就是顺序同步执行三个函数,性能检测只需加入时间
var start = new Date().getTime(); foo(); bar(); baz(); console.log(new Date().getTime()-start+'ms');
很简单,一点毛病都没
但是在实际情况中,为了不阻塞UI,js使用的大量的异步函数,例如:setTimeout,异步数据请求,在遇到异步函数时,执行时间检测显得捉襟见肘
var foo = function(){ setTimeout(doSomething, 2000); }, bar = function(){ http.get(...).subscribe(doSomething); }, baz = function(){ ... };
上面代码中,foo函数和bar函数均为异步执行,无法用上面简单粗暴的方法检测执行时间,也就是说无法追踪异步函数,所以angular搞了zonejs这么个东西,
- Zonejs
zonejs为异步函数提供了一个上下文环境,每一个异步的任务在zone.js都被当做为一个Task,并在Task的基础上zone.js为开发者提供了执行前后的钩子函数,这些钩子有
- onZoneCreated:已弃用
- beforeTask:已弃用
- afterTask:已弃用;
- onError:zone运行Task时候的异常钩子函数;
-
onScheduleTask:task开始的时候调用的钩子
-
onInvokeTask:task完成
只要我们嵌入zone.js
到我们的网站中,几乎所有导致异步操作的方法都在zone中运行。
zonejs对js大多原生的异步方法进行了包装,例如:
Zone.setInterval()
Zone.alert()
Zone.prompt()
Zone.requestAnimationFrame()
Zone.addEventListener()
Zone.removeEventListener()
我们调用setTimeout()
我们实际的调用Zone.setTimeout()
有了zone,实现上面的执行时间检测姜会容易很多,如下示例:
function doSomething() { console.log('Async task'); // setTimeout(doSomething, 2000); } function main() { profilingZoneSpec.reset(); foo(); setTimeout(doSomething, 2000); setTimeout(doSomething, 2000); } function foo() { console.log("foo"); } var profilingZoneSpec = (function () { var time = 0, timer = performance ? performance.now.bind(performance) : Date.now.bind(Date); return { onScheduleTask: function (parentZoneDelegate, currentZone, targetZone, task) { console.log(`schedule task at: ${time}`); return parentZoneDelegate.scheduleTask(targetZone, task); }, onInvokeTask: function (delegate, current, target, task, applyThis, applyArgs) { this.start = timer(); delegate.invokeTask(target, task, applyThis, applyArgs); time += timer() - this.start; console.info('-----', time); }, time: function () { return Math.floor(time * 100) / 100 + 'ms'; }, reset: function () { time = 0; } }; }()); Zone.current.fork(profilingZoneSpec).run(main);
输出结果为:
foo schedule task at: 0schedule task at: 0Async task ----- 0.8800000000001091Async task ----- 1.7549999999996544
对两次异步执行都挂了钩子,我们可以在task开始或者结束的时候做一些我们需要的事。
在zonejs中fork方法会产生一个继承至zone的子类,并在fork函数中可以配置特定的钩子方法,形成独立的zone上下文。而run方法则是启动执行业务代码的对外接口。
最后码一个zone的演讲视频:https://www.youtube.com/watch?v=3IqtmUscE_U
参考angular路由大神的博客:https://blog.thoughtram.io/angular/2016/01/22/understanding-zones.html (他文章中的有些API已废除,神坑,我一度怀疑自己电脑坏了)