拦截 zone 事件是棘手的,因为 zone 是在运行时生成的,并且标准方法(如子类化和猴子补丁)只有在设计时已知父 zone时才起作用。 为了更好地演示这个问题,我们假设想在run()方法的前后进行拦截,这样我们可以测量执行时间和在控制台记录 zone。
这是一个破例来说明这个问题。
// 在设计时,不可能知道哪个 zone 将是父 zone。 因此,父 zone 必须传递到构造函数中。
class TimingZone extends Zone {
constructor(parent) { super(parent, 'timingZone');}
// We would like to intercept the run, and so we overwrite it.
run() {
// Capture the start time
var start = performance.now();
// It may appear that calling super.run() is the right thing
// to do at this point, but the super.run(), internally must
// invoke parent.run. So super.run is just a red herring,
// and we should rather understand what are the consequences
// of parent.run(). See next example.
super.run(...arguments);
// Capture the end time
var end = performance.now();
// Print the duration, and the current zone.
console.log(this.name, 'Duration:', end - start);
}
}
class LogZone extends Zone {
constructor(parent) { super(parent, 'logZone');}
run() {
// log the zone name and 'enter'
console.log(this.name, 'enter');
// The issue with calling parent.run, is that it will cause the
// current zone to be changed to the parent zone.
// What we need is a way of calling the parent run hooks, without
// changing the current zone.
this.parent.run.apply(this, arguments);
// log the zone name and 'leave'
console.log(this.name, 'leave');
}
}
让我们使用上述 zone 创建一个简单的例子。
// Compose several zones.
let rootZone = Zone.current;
let timingZone = new TimingZone(rootZone);
let logZone = new LogZone(timingZone);
logZone.run(() => {
console.log(Zone.current.name, 'Hello World!');
});
这是人们对上述示例的期望。 具体而言,期望运行块内的当前区域将是 logZone 的区域。
这里是上面例子的输出
logZone enter
logZone Hello World;
logZone Duration: 0.123
logZone leave
为什么标准工具不起作用
拦截调用(super或父级)的标准方式不起作用的原因是父 zone 在设计时是未知的。 如果父 zone 在设计时间是已知的,那么我们可以这样写:
class TimingZone extends RootZone {
run() {
…
super.run(...arguments);
…
}
}
class LogZone extends TimingZone {
run() {
…
super.run(...arguments);
…
}
}
如果我们可以在设计时决定 zone 的层次结构,super.run()
的调用将按预期工作。 然而,因为父进程在运行时之前是未知的,所以我们需要将更改 zone 的行为与调用父钩子的行为分开。 为了解决这个问题,钩子被指定为fork() 的一部分,钩子接收一个父ZoneDelagate(只处理钩子)而不是一个父 zone (这将导致一个 zone 改变)。
Zone | ZoneDelegate | 描述 |
---|---|---|
run | invoke | 当执行zone run()块时,调用invoke() ZoneDelegate钩子。 这允许在不改变当前 zone 的情况下委派钩子。 |
wrap | intercept | 当zone wrap() 块被执行时,intercept() ZoneDelegate钩子被调用。 这允许委托钩子而不会重新包装回调。 |
下一节:为了跟踪任务,我们必须重新审视 setTimeout() 如何被猴子修补以修改跟踪 Zone 的方式。