任务调度

为了跟踪任务,我们必须重新审视 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() 拦截区域。