了解Zones

本文翻译自Pascal Precht的《Understanding Zones》

在NG-Conf 2014大会上,Brian进行了一场关于zones的精彩演讲,为我们很好的介绍了zones是如何改变我们处理异步代码的方式。如果你还没看过那个演讲视频,可以去看看,它只有差不多17分钟。API可能和现在有点不同了,但是语义和基本概念是一样的。在本文中,我们会去深入了解zones到底是如何工作的。

面临的问题

让我们先快速的了解下zones到底是什么。就Brian在他的演讲中所陈述的,zones基本上就是异步操作的执行上下文。实践证明,zones对于错误处理和分析非常有用。但这到底是什么意思呢?

为了理解概念中的执行上下文部分,我们需要一个zones到底是尝试解决什么问题的直观画面。让我们先一起来看看下面的这段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()的回调函数执行时间并不在我们的分析之中。这是因为异步操作被添加进了浏览器的事件队列,当事件轮询到该操作的时候,才会被执行。

如果你对这些知识点完全不了解,我们建议你去看一个关于浏览器的事件轮询是如何工作的精彩演讲视频。

那我们如何解决这个问题呢?我们需要一些技巧,允许我们无论异步操作在什么时候发生,都可以执行我们的分析代码。当然,我们也许可以手动地为每个异步操作创建并启动一个单独的定时器,但这些定时器会被作为异步操作被添加到代码序列,这将显得相当杂乱。

这就到了zones的用武之地了。Zones可以在每次执行一个操作(好比启动或停止一个计时器,或者保存一个堆栈追踪)的时候,让代码进入或退出一个zone。Zones可以在我们的代码内重写方法,甚至用独立的zone关联数据。

创建,分叉,扩展 Zones

Zones实际上是Dart的一个语言特性。然而,当Dart可以编译成JavaScript后,我们可以在JavaScript中实现同样的功能特性。Brian已经帮我们完成了这个工作。他为JavaScript创建了zone.js作为Zones的入口,而zone.js同样是Angular 2的一个依赖库。在我们尝试了解如何通过Zones分析我们代码示例前,让我们先来讨论下,zones是怎样创建的。

一旦我们将zone.js嵌入到我们的网页中,我们就获得了一个全局的zone对象。zone有一个run()方法,它可以将一个函数作为它的参数,而该函数将在该zone中执行。换而言之,如果我们想让我们的代码在zone中运行,我们可以向下面这样做:

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

zone.run(main);

Ok,非常棒!但是这是什么意思呢?现在我们除了多写了些代码外,结果其实没什么不同。然而,在这个时候,我们的代码是在一个zone中执行的(另一个执行上下文),就像我们之前了解的一样,。Zones可以在每次执行一个操作的时候,让代码进入或退出一个zone。
为了建立这些hooks,我们需要fork当前的zone.Fork一个zone会返回一个新的zone,这个新的zone是继承自父zone的。当然,fork一个zone也允许我们扩展返回的zone的行为。我们可以通过在zone对象上调用.fork()来fork一个zone。我们可以先看看下面的代码:

var myZone = zone.fork();

myZone.run(main);

通过fork得到的zone和原始zone有着相同的功能。让我们来尝试通过之前提到过的这些hooks来扩展我们的新zone。我们使用ZoneSpecification来定义hooks.我们可以利用下面的这些hooks:

  • onZoneCreated - 当zone被fork时运行
  • beforeTask - 在zones中的一个函数运行后执行
  • afterTask - 在zones中的一个函数运行后执行
  • onError - 当传给zone.run的函数抛出异常时执行

下面的代码扩展了zone,在每个任务执行的前后打印日志。

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

Oh 等等,这到底是个什么鬼?每个hooks都执行了两次?这是为什么?当然,我们已经了解到zone.run明显的也被当做了一个任务,这就是为什么前两个信息被打印。但是setTimeout()好像也被当成了一个任务。这怎么可能?

Monkey-patched Hooks

事实证明,还有一些其他的hook。实际上,他们又不仅仅是hook,而monkey-patched方法在全局作用域。只要我们在我们的网页中嵌入zone.js,几乎所有引发异步操作的方法都是nokey-patched,并在新的zone中运行。

就好像当我们调用setTimeout(),我们实际上调用了Zone.setTimeout(),这反过来通过zone.fork()创建了一个新的zone,而给定的处理程序就在这新的zone中执行。这就是为什么我们的hooks也被执行了,因为fork的zone中的处理程序会执行,而fork的zone简单的继承自父zone。
还有很多方法被zone.js重写,并作为hook提供给我们:

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

    我们可能想知道为什么像alert()和prompt()这样的方法也会被patched。就像前面提到的,这些pathched方法同时也是hook。我们能够通过fork一个zone改变和扩展他们,就像前面提到的befeTask和afterTask一样。这是非常强大的功能,因为当我们写测试的时候,我们可以截断alert()和prompt()的调用,并且改变他们的行为。

zone.js附带一个小型的DSL,它允许你增加zone hook,如果你对这些特殊的东西感兴趣,你可以去看看该项目的readme。

创建一个 Profiling Zone

我们最初的问题就是我们不能获取我们代码内异步任务的执行时间。现在使用Zones和它提供的API,我们完全可以创建一个zone来分析我们异步任务的CPU时间。幸运的是,在zone.js仓库中,我们已经有了一个完整的profiling zone实现。

代码如下:

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 speicification。这个例子增加了一个.time()和.reset()方法到zone,我们可以通过下面的方式在zone对象上调用:

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

+语法是DSL的速记,它允许我们扩展父zone的hook。很优雅哈?

阅读更多

Time Zones

02-17

DescriptionnnPrior to the late nineteenth century, time keeping was a purely local phenomenon. Each town would set their clocks to noon when the sun reached its zenith each day. A clockmaker or town clock would be the "official" time and the citizens would set their pocket watches and clocks to the time of the town - enterprising citizens would offer their services as mobile clock setters, carrying a watch with the accurate time to adjust the clocks in customer's homes on a weekly basis. Travel between cities meant having to change one's pocket watch upon arrival. nHowever, once railroads began to operate and move people rapidly across great distances, time became much more critical. In the early years of the railroads, the schedules were very confusing because each stop was based on a different local time. The standardization of time was essential to efficient operation of railroads. nnIn 1878, Canadian Sir Sanford Fleming proposed the system of worldwide time zones that we use today. He recommended that the world be divided into twenty-four time zones, each spaced 15 degrees of longitude apart. Since the earth rotates once every 24 hours and there are 360 degrees of longitude, each hour the earth rotates one-twenty-fourth of a circle or 15° of longitude. Sir Fleming's time zones were heralded as a brilliant solution to a chaotic problem worldwide. nnUnited States railroad companies began utilizing Fleming's standard time zones on November 18, 1883. In 1884 an International Prime Meridian Conference was held in Washington D.C. to standardize time and select the Prime Meridian. The conference selected the longitude of Greenwich, England as zero degrees longitude and established the 24 time zones based on the Prime Meridian. Although the time zones had been established, not all countries switched immediately. Though most U.S. states began to adhere to the Pacific, Mountain, Central, and Eastern time zones by 1895, Congress didn't make the use of these time zones mandatory until the Standard Time Act of 1918. nnToday, many countries operate on variations of the time zones proposed by Sir Fleming. All of China (which should span five time zones) uses a single time zone - eight hours ahead of Coordinated Universal Time (known by the abbreviation UTC - based on the time zone running through Greenwich at 0° longitude). Russia adheres to its designated time zones although the entire country is on permanent Daylight Saving Time and is an hour ahead of their actual zones. Australia uses three time zones - its central time zone is a half-hour ahead of its designated time zone. Several countries in the Middle East and South Asia also utilize half-hour time zones. nnSince time zones are based on segments of longitude and lines of longitude narrow at the poles, scientists working at the North and South Poles simply use UTC time. Otherwise, Antarctica would be divided into 24 very thin time zones! nnTime zones have recently been given standard capital-letter abbreviations as follows: nnUTC Coordinated Universal Time nGMT Greenwich Mean Time, defined as UTC nBST British Summer Time, defined as UTC+1 hour nIST Irish Summer Time, defined as UTC+1 hour nWET Western Europe Time, defined as UTC nWEST Western Europe Summer Time, defined as UTC+1 hour nCET Central Europe Time, defined as UTC+1 nCEST Central Europe Summer Time, defined as UTC+2 nEET Eastern Europe Time, defined as UTC+2 nEEST Eastern Europe Summer Time, defined as UTC+3 nMSK Moscow Time, defined as UTC+3 nMSD Moscow Summer Time, defined as UTC+4 nAST Atlantic Standard Time, defined as UTC-4 hours nADT Atlantic Daylight Time, defined as UTC-3 hours nNST Newfoundland Standard Time, defined as UTC-3.5 hours nNDT Newfoundland Daylight Time, defined as UTC-2.5 hours nEST Eastern Standard Time, defined as UTC-5 hours nEDT Eastern Daylight Saving Time, defined as UTC-4 hours nCST Central Standard Time, defined as UTC-6 hours nCDT Central Daylight Saving Time, defined as UTC-5 hours nMST Mountain Standard Time, defined as UTC-7 hours nMDT Mountain Daylight Saving Time, defined as UTC-6 hours nPST Pacific Standard Time, defined as UTC-8 hours nPDT Pacific Daylight Saving Time, defined as UTC-7 hours nHST Hawaiian Standard Time, defined as UTC-10 hours nAKST Alaska Standard Time, defined as UTC-9 hours nAKDT Alaska Standard Daylight Saving Time, defined as UTC-8 hours nAEST Australian Eastern Standard Time, defined as UTC+10 hours nAEDT Australian Eastern Daylight Time, defined as UTC+11 hours nACST Australian Central Standard Time, defined as UTC+9.5 hours nACDT Australian Central Daylight Time, defined as UTC+10.5 hours nAWST Australian Western Standard Time, defined as UTC+8 hours nnGiven the current time in one time zone, you are to compute what time it is in another time zone.nInputnnThe first line of input contains N, the number of test cases. For each case a line is given with a time, and 2 time zone abbreviations. Time is given in standard a.m./p.m. format with midnight denoted "midnight" and noon denoted "noon" (12:00 a.m. and 12:00 p.m. are oxymorons).nOutputnnFor each case, assuming the given time is the current time in the first time zone, give the current time in the second time zone.nSample Inputnn4nnoon HST CEST n11:29 a.m. EST GMTn6:01 p.m. CST UTCn12:40 p.m. ADT MSKnSample Outputnnmidnightn4:29 p.m.n12:01 a.m.n6:40 p.m.

没有更多推荐了,返回首页