Fork me on GitHub

初探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为开发者提供了执行前后的钩子函数,这些钩子有

  1. onZoneCreated:已弃用
  2. beforeTask:已弃用
  3. afterTask:已弃用;
  4. onError:zone运行Task时候的异常钩子函数;
  5. onScheduleTask:task开始的时候调用的钩子
  6. 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已废除,神坑,我一度怀疑自己电脑坏了)

posted @ 2018-02-01 17:50  devass  阅读(188)  评论(0编辑  收藏  举报