为了跟踪任务,我们必须重新审视 setTimeout() 如何被猴子修补以修改跟踪 Zone 的方式。
要跟踪任务,我们必须看下 setTimeout() 的猴子补丁如何修改被跟踪的 zone。
// 保存setTimeout的原始引用
let originalSetTimeout = window.setTimeout;
// 使用在 zone 中包含回调的函数覆盖API。
window.setTimeout = function(callback, delay) {
// 对当前 zone 使用scheduleTask API。
Zone.current.scheduleMacroTask(
// 调试信息
'setTimeout',
// 回调需要在当前 zone 执行。
callback,
// 可选数据,如任务是否重复。
null,
// 默认计划行为
(task) => {
return originalSetTimeout(
// Use the task invoke method, so that the task can
// call callback in the correct zone.
task.invoke,
// original delay information
delay
);
});
}
使用示例:
// Create a logging zone
let logZone = Zone.current.fork({
onScheduleTask: function(parentZoneDelegate, currentZone,
targetZone, task) {
// Print when async tasks are scheduled
console.log('Schedule', task.source);
return parentZoneDelegate.scheduleTask(targetZone, task);
},
onInvokeTask: function(parentZoneDelegate, currentZone,
targetZone, task, applyThis, applyArgs) {
// Print when async tasks are invoked
console.log('Invoke', task.source);
return parentZoneDelegate.invokeTask(
targetZone, applyThis, applyArgs);
}
});
console.log('start');
logZone.run(() => {
setTimeout(() => null, 0);
});
console.log('end');
输出结果
start
Schedule setTimeout
end
Invoke setTimeout
关键点:
- 所有调度任务的 API 都使用 Zone.prototype.scheduleTask() 而不是 Zone.prototype.wrap()。
- 任务使用 Zone.prototype.runGuarded() 进行回调执行,因此它们处理所有错误。
- 使用 invokeTask() 拦截任务,而使用 invoke() 拦截区域。