理解Zone

简介: 原文地址:http://blog.thoughtram.io/angular/2016/01/22/understanding-zones.html在NG-Conf 2014年,Brian介绍了Zone,以及它们如何改变我们处理异步代码的方式。

原文地址:http://blog.thoughtram.io/angular/2016/01/22/understanding-zones.html

在NG-Conf 2014年,Brian介绍了Zone,以及它们如何改变我们处理异步代码的方式。如果你还没看过这个演讲,试一试,只需要15分钟,api现在可能有所不同,但语义和底层概念都是相同的。在本文中,我们想深入地探讨zone如何的运作。

要解决的问题

让我们快速来概括下什么是Zone,正如Brian所说,它们基本上是一个异步操作的执行上下文,证明了它们在错误处理和分析非常有用,但这到底意味着什么呢?为了理解执行上下文这一部分,我们需要更好地了解Zone用来解决什么问题,我们先看看以下JavaScript代码。

foo();
bar();
baz();

function foo() {...}
function bar() {...}
function baz() {...}

在这里没什么特别的代码,我们有三个连续执行的函数foo,bar,baz,比如,我们要测量这个代码的执行时间,我们很容易的扩展一些用来分析的代码段。

var start,
    time = 0;
    timer = performance ? performance.now || Date.now;

// start timer
start = timer();
foo();
bar();
baz();
// stop timer
time = timer() - start;
// log time in ms
console.log(Math.floor(time*100) / 100 + 'ms');

然而,我们常常有异步操作要做。可以是AJAX请求从远程服务器获取一些数据,或者也许我们只是为下一帧执行一些操作,无论发生哪种异步操作,因为是异步,基本上,这些操作无法被我们的分析代码给计算到,看看这个代码段

function doSomething() {
  console.log('Async task');
}

// start timer
start = timer();
foo();
setTimeout(doSomething, 2000);
bar();
baz();
// stop timer
time = timer() - start;

我们在代码中添加了一个异步操作,这对我们的分析有什么影响?我们会发现分析结果没有什么大的差别。

事实上多了一个操作,所以需要更长的时间执行这段代码,然而实际执行的时间没有计算到setTimeout()操作,这是因为异步操作会被添加到浏览器的事件队列,在下一次事件循环(event loops)中才会被执行。

如果你还不了解这块内容,你可以看看这个视频浏览器事件循环是如何工作的

那么,我们如何解决这个问题,我们需要的一些hook,允许我们在这样的异步任务发生时执行一些分析代码,当然,我们可以手动的为每个异步创建并启动一个计时器,但这会使我们的代码变得非常混乱。

这就是Zone可以发挥作用的地方,Zone可以执行一些操作 - 如在每次代码进入或退出一个区域,启动、停止计时器,或保存堆栈跟踪,他们可以在我们的代码中重写方法,甚至关联起各个区域的数据。

创建(Creating),分叉(forking),扩展(extending) Zone

Zone实际上Dart语言的特性,然后,由于Dart也只是编译成JavaScript,所以我们在Javascript中也能实现相同功能,Brian做到了这点,他为Javascript Zone 创建了 Zone.js,也是一个Angular 2的依赖。在使用Zone为我们的示例代码创建分析代码之前,先让我们讨论如何创建zone。

一旦我们嵌入zone.js到我们的网站,我们可以获得全局zone对象。zone配备了一个run()方法,它接受一个函数用来在这个zone区域中执行,也就是说,我们想要在一个zone中运行代码,我们可以这样做:

function main() {
  foo();
  setTimeout(doSomething, 2000);
  bar();
  baz();
}

zone.run(main);

酷。但这有什么意义?好吧……目前的结果没有什么区别,除了我们不得不写下更多的代码。但是,在此时,我们的代码运行在一个zone中(另一个执行上下文),正如我们前面了解到的,当我们的代码进入或退出某个zone时,zone可以对其进行操作。

为了建立这些hook,我们需要fork当前的zone,fork一个zone会返回一个新的zone,它基本上是从“父”zone继承的,当然,fork一个zone也允许我们扩展返回的那个zone的行为,我们可以在zone对象上使用.fork()来fork一个zone,这里的代码看上去可能是这样的:

var myZone = zone.fork();

myZone.run(main);

这实际上只是给了我们一个新的zone,和原先的zone(我们还没有讨论过)相同功能。让我们来尝试这些我们之前提到的hook,并扩展我们的新zone,使用一个ZoneSpecification来定义hook,并传递给fork(),我们可以使用下面这些hook:

  • onZoneCreated - zone被fork时调用
  • beforeTask - 在zone.run执行的函数之前调用
  • afterTask - 在zone.run执行的函数之后调用
  • onError - 当函数传递给run或beforeTask抛出异常时被调用。

下面是我们的示例代码,在每个任务执行之前和之后:

var myZoneSpec = {
  beforeTask: function () {
    console.log('Before task');
  },
  afterTask: function () {
    console.log('After task');
  }
};

var myZone = zone.fork(myZoneSpec);
myZone.run(main);

// Logs:
// Before task
// After task
// Before task
// Async task
// After task

等一下!发生了什么?这两个hook被执行了两次? 这是为什么?当然,我们已经了解到,zone.run显然被认为是一个“task”,这也就是为什么前两个消息被log,但似乎像setTimeout()调用也被视为一个task了。这怎么可能?

猴子补丁(Monkey-patched) hook

Monkey-patched是指给内置对象扩展的一种术语

原来还有一些其他的hook,实际上,这些都不只是简单的hook,还在全局作用域中monkey-patched一些方法,只要我们在网站上嵌入zone.js,几乎导致所有的异步操作方法被monkey-patched,并都运行在一个新的zone里。

例如,当我们调用setTimeout(),实际上我们调用的是Zone.setTimeout(), 这又使用zone.fork()创建了一个新的zone,其给定的处理程序被执行。这就是为什么我们的hook被很好的执行了,因为这个被fork的zone从父zone继承了要执行的task。

默认情况下zone.js重写了提供了如下的方法:

  • Zone.setInterval()
  • Zone.alert()
  • Zone.prompt()
  • Zone.requestAnimationFrame()
  • Zone.addEventListener()
  • Zone.removeEventListener()

可能有人会问,为什么像方法alert()prompt()也被修补,如前所述,这些hook同时修补方法,我们可以已添加afterTask和afterTask完全相同的方式,改变和扩展它们fork的zone,这是非常强大的,当我们编写测试时,我们可以截获alert()prompt(),并改变它们自己的行为。

zone.js配备了一个微型的DSL,让你可以加强zone hook,如果你对这个特别的东西感兴趣,你可以看看这个项目的readme

创建Zone性能分析

我们最初的问题是,我们能不能捕捉到我们代码中异步任务的执行时间,现在我们已经了解关于Zone和它提供的api,实际上我们需要创建一个zone,用来记录我们异步任务的CPU时间,幸运的是,一个zone性能分析的实现在zone.js资源库例子中已经实现,你可以在这里找到

在这看上去是这样的:

var profilingZone = (function () {
  var time = 0,
      timer = performance ?
                  performance.now.bind(performance) :
                  Date.now.bind(Date);
  return {
    beforeTask: function () {
      this.start = timer();
    },
    afterTask: function () {
      time += timer() - this.start;
    },
    time: function () {
      return Math.floor(time*100) / 100 + 'ms';
    },
    reset: function () {
      time = 0;
    }
  };
}());

和我们在本文的开头的代码几乎相同,只是把他放在zone specification内,这个例子还增加了add()reset()方法,调用zone对象看上去是这样的:

zone
  .fork(profilingZone)
  .fork({
    '+afterTask': function () {
      console.log('Took: ' + zone.time());
    }
  })
  .run(main);

+语法是一个DSL,它允许扩展父zone的hook

我们还可以使用一个LongStackTraceZone,当然还有更多的例子

相关文章
|
2月前
|
Java 数据库连接 数据库
The server time zone value ‘Öйú±ê׼ʱ¼ä‘ is unrecognized or represents more than one time zone
The server time zone value ‘Öйú±ê׼ʱ¼ä‘ is unrecognized or represents more than one time zone
23 0
|
11月前
|
Java 关系型数据库 MySQL
The server time zone value ‘?й???’ is unrecognized or represents more than one time zone. You must c
报错信息如下:The server time zone value ‘?й???’ is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
66 0
|
2月前
|
数据库连接
数据库连接的时区问题 The server time zone value is unrecognized
数据库连接的时区问题 The server time zone value is unrecognized
27 0
|
11月前
|
容灾 数据库 数据安全/隐私保护
在单 Zone 模式
在单 Zone 模式
88 1
|
11月前
|
容灾 数据库 数据安全/隐私保护
一个单 Zone 的集群
一个单 Zone 的集群
74 1
|
关系型数据库 MySQL Java
超详解The server time zone value ‘�й���׼ʱ��‘ is unrecognized or represents more than one time zone
超详解The server time zone value ‘�й���׼ʱ��‘ is unrecognized or represents more than one time zone
317 0
|
数据库连接 数据库
The server time zone value ‘‘ is unrecognized or represents more than one time zone.
The server time zone value ‘‘ is unrecognized or represents more than one time zone.
|
关系型数据库 MySQL Java
The server time zone value '?й???????' is unrecognized or represents more than one time zone.
The server time zone value '?й???????' is unrecognized or represents more than one time zone.
75 0
The server time zone value '?й???????' is unrecognized or represents more than one time zone.
|
关系型数据库 MySQL Java
The server time zone value ‘锟叫癸拷锟斤拷\u05FC时锟斤拷‘ is unrecognized or represents more than one time zone
The server time zone value ‘锟叫癸拷锟斤拷\u05FC时锟斤拷‘ is unrecognized or represents more than one time zone
The server time zone value ‘锟叫癸拷锟斤拷\u05FC时锟斤拷‘ is unrecognized or represents more than one time zone
|
Java 数据库连接
JDBC - The server time zone value ‘???‘ is unrecognized or represents more than one time zone
JDBC - The server time zone value ‘???‘ is unrecognized or represents more than one time zone
83 0