源码级别解析 · zone.ts · zone-impl.ts · promise.ts · timers.ts
2026-03-19 | 每日技术深度解读
Zone.js 是 Angular 团队开发的异步执行上下文追踪库
核心职责:
本质: Monkey Patch 所有浏览器异步 API,在调用前后注入钩子
四大核心:Zone、ZoneDelegate、ZoneSpec、Task
| 概念 | 职责 | 类比 |
|---|---|---|
| Zone | 执行上下文,管理异步调用链 | 线程本地存储 (ThreadLocal) |
| ZoneSpec | Zone 的配置规则和钩子函数 | 拦截器配置 |
| ZoneDelegate | 委托模式,协调父子 Zone | 事件冒泡机制 |
| Task | 异步任务抽象 | VM 任务队列 |
interface Zone {
readonly parent: Zone | null; // 父 Zone
readonly name: string; // Zone 名称(调试用)
get(key: string): any; // 获取属性
getZoneWith(key: string): Zone; // 查找定义 key 的 Zone
fork(zoneSpec: ZoneSpec): Zone; // 创建子 Zone
wrap<F>(callback: F, source: string): F; // 包装回调
run<T>(callback, ...args): T; // 在 Zone 中执行
runGuarded<T>(callback, ...args): T; // 执行 + 错误处理
runTask(task, ...args): any; // 执行任务
scheduleMicroTask(...): MicroTask;
scheduleMacroTask(...): MacroTask;
scheduleEventTask(...): EventTask;
scheduleTask<T>(task: T): T;
cancelTask(task: Task): any;
}
class ZoneImpl implements Zone {
private _parent: ZoneImpl | null;
private _name: string;
private _properties: {[key: string]: any};
private _zoneDelegate: _ZoneDelegate;
constructor(parent: ZoneImpl | null, zoneSpec: ZoneSpec | null) {
this._parent = parent;
this._name = zoneSpec?.name || '<root>';
this._properties = zoneSpec?.properties || {};
this._zoneDelegate = new _ZoneDelegate(
this,
this._parent?._zoneDelegate,
zoneSpec
);
}
static get current(): Zone {
return _currentZoneFrame.zone; // 从栈帧获取当前 Zone
}
}
关键:_currentZoneFrame 是全局变量,存储当前执行上下文的 Zone 栈帧
public fork(zoneSpec: ZoneSpec): Zone {
if (!zoneSpec) throw new Error('ZoneSpec required!');
return this._zoneDelegate.fork(this, zoneSpec);
}
// ZoneDelegate.fork 实现
fork(targetZone: Zone, zoneSpec: ZoneSpec): Zone {
return this._forkZS
? this._forkZS.onFork!(this._forkDlgt!, this.zone, targetZone, zoneSpec)
: new ZoneImpl(targetZone, zoneSpec); // 默认行为
}
fork 流程:
public run<T>(
callback: (...args: any[]) => T,
applyThis?: any,
applyArgs?: any[],
source?: string
): T {
// 1. 保存当前栈帧,切换到新 Zone
_currentZoneFrame = { parent: _currentZoneFrame, zone: this };
try {
// 2. 通过委托执行回调
return this._zoneDelegate.invoke(
this, callback, applyThis, applyArgs, source
);
} finally {
// 3. 恢复原栈帧(无论成功或异常)
_currentZoneFrame = _currentZoneFrame.parent!;
}
}
Zone 栈帧链: 类似调用栈,_currentZoneFrame 形成链表结构
public runGuarded<T>(
callback: (...args: any[]) => T,
applyThis: any = null,
applyArgs?: any[],
source?: string
): T {
_currentZoneFrame = { parent: _currentZoneFrame, zone: this };
try {
try {
return this._zoneDelegate.invoke(
this, callback, applyThis, applyArgs, source
);
} catch (error) {
// 捕获错误,交给 Zone 错误处理
if (this._zoneDelegate.handleError(this, error)) {
throw error; // 如果未被处理,重新抛出
}
}
} finally {
_currentZoneFrame = _currentZoneFrame.parent!;
}
}
public wrap<T extends Function>(callback: T, source: string): T {
if (typeof callback !== 'function') {
throw new Error('Expecting function got: ' + callback);
}
// 1. 通过委托拦截回调
const _callback = this._zoneDelegate.intercept(this, callback, source);
const zone: ZoneImpl = this;
// 2. 返回包装函数,保证在原 Zone 中执行
return function(this: unknown) {
return zone.runGuarded(_callback, this, arguments, source);
} as any as T;
}
用途: 将回调绑定到特定 Zone,跨异步操作保持上下文
例如: setTimeout 的回调被 wrap 后,执行时恢复原 Zone
interface ZoneSpec {
name: string; // Zone 名称(必须)
properties?: {[key: string]: any}; // 自定义属性
// 钩子函数(全部可选)
onFork?: (parentDelegate, current, target, spec) => Zone;
onIntercept?: (parentDelegate, current, target, delegate, source) => Function;
onInvoke?: (parentDelegate, current, target, delegate, this, args, source) => any;
onHandleError?: (parentDelegate, current, target, error) => boolean;
onScheduleTask?: (parentDelegate, current, target, task) => Task;
onInvokeTask?: (parentDelegate, current, target, task, this, args) => any;
onCancelTask?: (parentDelegate, current, target, task) => any;
onHasTask?: (parentDelegate, current, target, hasTaskState) => void;
}
| 钩子 | 触发时机 | 典型用途 |
|---|---|---|
| onFork | fork() 调用时 | 自定义子 Zone 创建逻辑 |
| onIntercept | wrap() 调用时 | 修改或记录回调 |
| onInvoke | run()/runGuarded() | 性能监控、日志 |
| onHandleError | 异常发生时 | 全局错误处理 |
| onScheduleTask | 调度任务时 | 任务拦截、Mock |
| onHasTask | 任务队列状态变化 | 检测稳定性(Angular 核心用途) |
为什么需要 ZoneDelegate?
子 Zone 不能直接调用父 Zone 的方法,否则回调会被绑定到父 Zone
interface ZoneDelegate {
zone: Zone;
fork(targetZone: Zone, zoneSpec: ZoneSpec): Zone;
intercept(targetZone: Zone, callback: Function, source: string): Function;
invoke(targetZone: Zone, callback: Function, ...): any;
handleError(targetZone: Zone, error: any): boolean;
scheduleTask(targetZone: Zone, task: Task): Task;
invokeTask(targetZone: Zone, task: Task, ...): any;
cancelTask(targetZone: Zone, task: Task): any;
hasTask(targetZone: Zone, isEmpty: HasTaskState): void;
}
class _ZoneDelegate implements ZoneDelegate {
private _taskCounts: {[key in TaskType]: number} = {
'microTask': 0, 'macroTask': 0, 'eventTask': 0
};
// 存储各钩子的 ZoneSpec 和委托链
private _forkZS: ZoneSpec | null;
private _forkDlgt: _ZoneDelegate | null;
private _forkCurrZone: Zone | null;
// ... 其他钩子的存储
constructor(zone: Zone, parentDelegate: _ZoneDelegate | null, zoneSpec: ZoneSpec | null) {
// 决定使用当前 ZoneSpec 还是委托给父级
this._forkZS = zoneSpec?.onFork ? zoneSpec : parentDelegate?._forkZS;
this._forkDlgt = zoneSpec?.onFork ? parentDelegate : parentDelegate?._forkDlgt;
// ...
}
}
当前栈清空后立即执行
特点:不可取消,保证执行一次
在下一个 VM 轮次执行
特点:可取消,有延迟
事件监听器,可能执行零次或多次
特点:不可预测的执行时机
class ZoneTask<T extends TaskType> implements Task {
public type: T;
public source: string;
public callback: Function;
public invoke: Function;
public data?: TaskData;
public scheduleFn?: (task: Task) => void;
public cancelFn?: (task: Task) => void;
public runCount: number = 0;
_zone: ZoneImpl | null = null;
_zoneDelegates: _ZoneDelegate[] | null = null;
_state: TaskState = 'notScheduled';
constructor(type, source, callback, options, scheduleFn, cancelFn) {
this.invoke = function() {
return ZoneTask.invokeTask.call(global, self, this, arguments);
};
}
}
scheduleMicroTask(
source: string,
callback: Function,
data?: TaskData,
customSchedule?: (task: Task) => void
): MicroTask {
return this.scheduleTask(
new ZoneTask(microTask, source, callback, data, customSchedule, undefined)
);
}
// 内部 scheduleMicroTask 实现(zone-impl.ts)
function scheduleMicroTask(task?: MicroTask) {
// 使用原生 Promise 或 queueMicrotask 调度
if (nativeMicroTask) {
nativeMicroTask(task.invoke);
} else {
nativePromise.resolve().then(task.invoke);
}
}
scheduleMacroTask(
source: string,
callback: Function,
data?: TaskData,
customSchedule?: (task: Task) => void,
customCancel?: (task: Task) => void
): MacroTask {
return this.scheduleTask(
new ZoneTask(macroTask, source, callback, data, customSchedule, customCancel)
);
}
// 使用示例:setTimeout 内部
const task = scheduleMacroTaskWithCurrentZone(
'setTimeout',
callback,
{ delay, isPeriodic: false },
scheduleTask, // 调用原生 setTimeout
clearTask // 调用原生 clearTimeout
);
scheduleEventTask(
source: string,
callback: Function,
data?: TaskData,
customSchedule?: (task: Task) => void,
customCancel?: (task: Task) => void
): EventTask {
return this.scheduleTask(
new ZoneTask(eventTask, source, callback, data, customSchedule, customCancel)
);
}
// 使用示例:addEventListener 内部
const task = zone.scheduleEventTask(
'addEventListener',
listener,
{ target, eventName },
() => nativeAddEventListener.call(target, eventName, task.invoke),
() => nativeRemoveEventListener.call(target, eventName, task.invoke)
);
runTask(task: Task, applyThis?: any, applyArgs?: any): any {
if (task.zone != this) {
throw new Error('A task can only run in its creation zone!');
}
const {type, data: {isPeriodic, isRefreshable} = {}} = task;
// 状态转换: scheduled -> running
zoneTask._transitionTo(running, scheduled);
_currentTask = zoneTask;
_currentZoneFrame = { parent: _currentZoneFrame, zone: this };
try {
return this._zoneDelegate.invokeTask(this, zoneTask, applyThis, applyArgs);
} finally {
// 状态恢复: running -> scheduled (EventTask) 或 notScheduled (一次性 MacroTask)
if (type == eventTask || isPeriodic) {
zoneTask._transitionTo(scheduled, running);
} else {
this._updateTaskCount(zoneTask, -1);
zoneTask._transitionTo(notScheduled, running);
}
_currentZoneFrame = _currentZoneFrame.parent!;
_currentTask = previousTask;
}
}
cancelTask(task: Task): any {
if (task.zone != this) {
throw new Error('A task can only be cancelled in its creation zone!');
}
if (task.state !== scheduled && task.state !== running) {
return; // 已完成或已取消的任务不能再次取消
}
// 状态转换: scheduled/running -> canceling
zoneTask._transitionTo(canceling, scheduled, running);
try {
this._zoneDelegate.cancelTask(this, task); // 调用 cancelFn
} catch (err) {
zoneTask._transitionTo(unknown, canceling); // 错误时状态为 unknown
throw err;
}
this._updateTaskCount(zoneTask, -1); // 减少任务计数
zoneTask._transitionTo(notScheduled, canceling);
task.runCount = -1; // 标记为已取消
}
type HasTaskState = {
microTask: boolean;
macroTask: boolean;
eventTask: boolean;
change: TaskType; // 哪种类型触发了变化
};
// ZoneDelegate._updateTaskCount
_updateTaskCount(type: TaskType, count: number) {
const counts = this._taskCounts;
const prev = counts[type];
const next = counts[type] = prev + count;
// 当从 0->1 或 1->0 时触发 onHasTask
if (prev == 0 || next == 0) {
const isEmpty: HasTaskState = {
microTask: counts['microTask'] > 0,
macroTask: counts['macroTask'] > 0,
eventTask: counts['eventTask'] > 0,
change: type
};
this.hasTask(this._zone, isEmpty);
}
}
Angular 用途: 检测应用稳定性(所有任务队列为空时触发变更检测)
核心思路: 替换全局 Promise 为 ZoneAwarePromise
// 保存原生 Promise
const NativePromise = global['Promise'];
// 替换为 Zone 感知的 Promise
global['Promise'] = ZoneAwarePromise;
// 同时 patch 原生 Promise 的 then
patchThen(NativePromise);
// patchThen 实现
function patchThen(Ctor: Function) {
const proto = Ctor.prototype;
const originalThen = proto.then;
proto.then = function(onResolve, onReject) {
const wrapped = new ZoneAwarePromise((resolve, reject) => {
originalThen.call(this, resolve, reject);
});
return wrapped.then(onResolve, onReject); // 走 ZoneAwarePromise.then
};
}
class ZoneAwarePromise<R> implements Promise<R> {
// 使用 Symbol 存储私有状态,避免冲突
[symbolState] = UNRESOLVED; // null = pending, true = resolved, false = rejected
[symbolValue] = []; // 回调队列
constructor(executor) {
try {
executor(
once(makeResolver(this, RESOLVED)),
once(makeResolver(this, REJECTED))
);
} catch (error) {
resolvePromise(this, false, error);
}
}
then(onFulfilled?, onRejected?) {
const chainPromise = new ZoneAwarePromise(noop);
const zone = Zone.current; // 捕获当前 Zone!
if (this[symbolState] == UNRESOLVED) {
this[symbolValue].push(zone, chainPromise, onFulfilled, onRejected);
} else {
scheduleResolveOrReject(this, zone, chainPromise, onFulfilled, onRejected);
}
return chainPromise;
}
}
function scheduleResolveOrReject(
promise, zone, chainPromise, onFulfilled, onRejected
) {
const promiseState = promise[symbolState];
const delegate = promiseState
? typeof onFulfilled === 'function' ? onFulfilled : forwardResolution
: typeof onRejected === 'function' ? onRejected : forwardRejection;
// 关键:在捕获的 Zone 中调度微任务
zone.scheduleMicroTask(
'Promise.then',
() => {
try {
const value = zone.run(delegate, undefined, [promise[symbolValue]]);
resolvePromise(chainPromise, true, value);
} catch (error) {
resolvePromise(chainPromise, false, error);
}
},
chainPromise as TaskData
);
}
核心: Promise.then 回调通过 scheduleMicroTask 在原 Zone 中执行
function resolvePromise(promise, state, value) {
if (promise === value) {
throw new TypeError('Promise resolved with itself');
}
if (promise[symbolState] === UNRESOLVED) {
promise[symbolState] = state;
const queue = promise[symbolValue];
promise[symbolValue] = value;
// 处理已排队的回调
for (let i = 0; i < queue.length; ) {
scheduleResolveOrReject(
promise, queue[i++], queue[i++], queue[i++], queue[i++]
);
}
// 未处理的 rejection
if (queue.length == 0 && state == REJECTED) {
promise[symbolState] = REJECTED_NO_CATCH;
const error = new Error('Uncaught (in promise): ' + value);
error.rejection = value;
error.promise = promise;
error.zone = Zone.current;
error.task = Zone.currentTask;
_uncaughtPromiseErrors.push(error);
api.scheduleMicroTask(); // 触发错误处理
}
}
}
export function patchTimer(window, setName, cancelName, nameSuffix) {
let setNative = patchMethod(window, setName, (delegate) =>
function(self, args) {
if (typeof args[0] === 'function') {
const options = {
isPeriodic: nameSuffix === 'Interval',
delay: args[1] || 0,
args: args
};
// 包装回调
const callback = args[0];
args[0] = function() {
try {
return callback.apply(this, arguments);
} finally {
// 非周期任务完成后清理
if (!options.isPeriodic) {
delete tasksByHandleId[options.handleId];
}
}
};
// 创建宏任务
const task = scheduleMacroTaskWithCurrentZone(
setName, args[0], options, scheduleTask, clearTask
);
return task.data.handleId;
}
return delegate.apply(window, args);
}
);
}
function scheduleTask(task: Task) {
const data = task.data;
// 将回调替换为 task.invoke
data.args[0] = function() {
return task.invoke.apply(this, arguments);
};
// 调用原生 setTimeout
const handleId = setNative.apply(window, data.args);
data.handleId = handleId;
// 保存 task 映射,用于 clearTimeout
tasksByHandleId[handleId] = task;
return task;
}
// task.invoke 内部
this.invoke = function() {
return ZoneTask.invokeTask.call(global, self, this, arguments);
};
// 最终调用 zone.runTask(task)
// setInterval 的 isPeriodic = true
const options = {
isPeriodic: nameSuffix === 'Interval', // true
delay: args[1] || 0,
args: args
};
// runTask 中的处理
if (type == eventTask || isPeriodic) {
// 周期任务执行后状态回到 scheduled
zoneTask._transitionTo(scheduled, running);
} else {
// 一次性任务执行后状态变为 notScheduled
this._updateTaskCount(zoneTask, -1);
zoneTask._transitionTo(notScheduled, running);
}
// finally 块中的清理
if (!isPeriodic && !isRefreshable) {
delete tasksByHandleId[handleId]; // 只有非周期任务才清理
}
Zone.__load_patch('EventTarget', (global, Zone, api) => {
patchEvent(global, api);
eventTargetPatch(global, api);
});
// eventTargetPatch 实现
export function eventTargetPatch(_global, api) {
const EVENT_TARGET = _global['EventTarget'];
api.patchEventTarget(_global, api, [EVENT_TARGET.prototype]);
}
// patchEventTarget 内部
function patchEventTarget(global, api, apis) {
for (const obj of apis) {
// 拦截 addEventListener/removeEventListener
patchMethod(obj, 'addEventListener', (delegate) =>
function(self, args) {
const [eventName, callback, options] = args;
// 创建 EventTask 替代原回调
const task = zone.scheduleEventTask(eventName, callback, ...);
args[1] = task.invoke; // 替换回调
return delegate.apply(self, args);
}
);
}
}
// 简化的 addEventListener 包装
const nativeAdd = target.addEventListener;
target.addEventListener = function(eventName, callback, options) {
if (typeof callback !== 'function') {
return nativeAdd.call(this, eventName, callback, options);
}
const zone = Zone.current;
// 创建 EventTask
const task = zone.scheduleEventTask(
'addEventListener:' + eventName,
callback,
{ target: this, eventName, options },
// scheduleFn: 调用原生 addEventListener
() => nativeAdd.call(this, eventName, task.invoke, options),
// cancelFn: 调用原生 removeEventListener
() => nativeRemove.call(this, eventName, task.invoke, options)
);
// 保存 task 引用用于移除
callback[zoneSymbol('listener')] = task;
};
// removeEventListener
target.removeEventListener = function(eventName, callback) {
const task = callback[zoneSymbol('listener')];
if (task) {
task.zone.cancelTask(task);
}
};
XHR 作为 MacroTask 处理
XMLHttpRequest.send() 被包装为宏任务,在请求完成时触发
Zone.__load_patch('XHR', (global, Zone) => {
const XHR_TASK = zoneSymbol('xhrTask');
const XHR_SYNC = zoneSymbol('xhrSync');
// patch open 方法,记录 URL 和同步/异步
patchMethod(XMLHttpRequestPrototype, 'open', () =>
function(self, args) {
self[XHR_SYNC] = args[2] === false; // 第三个参数是否同步
self[XHR_URL] = args[1];
return openNative.apply(self, args);
}
);
// patch send 方法
patchMethod(XMLHttpRequestPrototype, 'send', () =>
function(self, args) {
if (self[XHR_SYNC]) {
return sendNative.apply(self, args); // 同步请求不创建任务
}
// 异步请求创建宏任务
const task = scheduleMacroTaskWithCurrentZone('XMLHttpRequest.send', ...);
}
);
});
function scheduleTask(task: Task) {
const data = task.data;
const target = data.target;
// 添加 readystatechange 监听器
const listener = () => {
if (target.readyState === target.DONE) {
if (task.state === 'scheduled') {
task.invoke(); // 触发 Zone.runTask
}
}
};
oriAddListener.call(target, 'readystatechange', listener);
// 调用原生 send
sendNative.apply(target, data.args);
target[XHR_SCHEDULED] = true;
return task;
}
// 任务完成后的处理
// task.invoke() 会触发 onHasTask,通知 Angular 变更检测
static __load_patch(name: string, fn: PatchFn, ignoreDuplicate = false): void {
if (patches.hasOwnProperty(name)) {
const checkDuplicate = global[__symbol__('forceDuplicateZoneCheck')] === true;
if (!ignoreDuplicate && checkDuplicate) {
throw Error('Already loaded patch: ' + name);
}
} else if (!global['__Zone_disable_' + name]) {
const perfName = 'Zone:' + name;
mark(perfName);
patches[name] = fn(global, ZoneImpl, _api); // 执行补丁函数
performanceMeasure(perfName, perfName);
}
}
// 使用示例
Zone.__load_patch('timers', (global, Zone, api) => {
patchTimer(global, 'set', 'clear', 'Timeout');
patchTimer(global, 'set', 'clear', 'Interval');
});
export function patchBrowser(Zone: ZoneType): void {
// 1. Timer 补丁
Zone.__load_patch('timers', ...);
Zone.__load_patch('requestAnimationFrame', ...);
// 2. 阻塞方法
Zone.__load_patch('blocking', ...); // alert, prompt, confirm
// 3. EventTarget
Zone.__load_patch('EventTarget', ...);
// 4. Observer 类
Zone.__load_patch('MutationObserver', ...);
Zone.__load_patch('IntersectionObserver', ...);
Zone.__load_patch('FileReader', ...);
// 5. 属性补丁
Zone.__load_patch('on_property', ...); // onclick 等
// 6. 自定义元素
Zone.__load_patch('customElements', ...);
// 7. XHR
Zone.__load_patch('XHR', ...);
// 8. 其他
Zone.__load_patch('geolocation', ...);
Zone.__load_patch('PromiseRejectionEvent', ...);
Zone.__load_patch('queueMicrotask', ...);
}
NgZone 是 Angular 对 Zone.js 的封装,提供更友好的 API
// 简化的 NgZone 实现
class NgZone {
private _nesting = 0;
private _outer: Zone;
private _inner: Zone;
readonly hasPendingMacrotasks: boolean;
readonly hasPendingMicrotasks: boolean;
readonly isStable: boolean;
constructor() {
this._outer = Zone.current;
this._inner = this._outer.fork({
name: 'angular',
onHasTask: (delegate, current, target, hasTaskState) => {
// 当任务队列状态变化时,触发变更检测
if (!hasTaskState.microTask && !hasTaskState.macroTask) {
this._onMicrotaskEmpty.emit();
if (!this.hasPendingMacrotasks) {
this._onStable.emit();
}
}
}
});
}
}
class NgZone {
// 在 Zone 外执行(不触发变更检测)
runOutsideAngular<T>(fn: () => T): T {
return this._outer.run(fn);
}
// 在 Zone 内执行(触发变更检测)
run<T>(fn: () => T): T {
return this._inner.run(fn);
}
// run + 错误处理
runGuarded<T>(fn: () => T): T {
return this._inner.runGuarded(fn);
}
// 调度任务
scheduleMicroTask(fn: () => void) {
this._inner.scheduleMicroTask('scheduleMicrotask', fn);
}
}
runOutsideAngular: 用于性能敏感操作(如动画、高频事件)
// ApplicationRef 内部
class ApplicationRef {
private _runningTick = false;
constructor(private _zone: NgZone) {
// 监听 Zone 稳定事件
this._zone.onStable.subscribe(() => {
if (!this._zone.hasPendingMacrotasks && !this._zone.hasPendingMicrotasks) {
this.tick(); // 触发变更检测
}
});
}
tick(): void {
if (this._runningTick) {
throw new Error('ApplicationRef.tick is called recursively');
}
this._runningTick = true;
try {
// 遍历所有组件,执行变更检测
for (const view of this._views) {
view.detectChanges();
}
} finally {
this._runningTick = false;
}
}
}
// Zone.js 提供增强的错误堆栈
interface Error {
zoneAwareStack?: string; // 带有 Zone 信息的堆栈
originalStack?: string; // 原始堆栈
}
// 在错误发生时记录 Zone 信息
if (state === REJECTED && value instanceof Error) {
const trace = Zone.currentTask?.data?.['__creationTrace__'];
if (trace) {
ObjectDefineProperty(value, CURRENT_TASK_TRACE_SYMBOL, {
value: trace
});
}
}
// 长堆栈追踪(需要启用 longStackTraceZone)
Zone.longStackTraceZone = Zone.current.fork({
name: 'longStackTrace',
onScheduleTask: (delegate, current, target, task) => {
task.data['__creationTrace__'] = new Error().stack;
return delegate.scheduleTask(target, task);
}
});
// ZoneSpec.onError 钩子
const angularZone = Zone.current.fork({
name: 'angular',
onHandleError: (delegate, current, target, error) => {
// 1. 记录错误
console.error('Error in Zone:', current.name, error);
// 2. 通知 ErrorHandler
errorHandler.handleError(error);
// 3. 返回 false 表示已处理,不再抛出
return false;
}
});
// 未处理的 Promise rejection
api.onUnhandledError = (e) => {
if (api.showUncaughtError()) {
console.error(
'Unhandled Promise rejection:',
e.rejection,
'; Zone:', e.zone.name,
'; Task:', e.task?.source
);
}
};
// 禁用特定补丁
window.__Zone_disable_XHR = true; // 在加载 zone.js 之前
// 或使用 zone.js/testing 排除
import 'zone.js/testing/none'; // 完全禁用
Zoneless 是 Angular 的未来方向,移除 Zone.js 依赖
// 启用 Zoneless
bootstrapApplication(AppComponent, {
providers: [
provideExperimentalZonelessChangeDetection()
]
});
// 使用 Signal 触发变更检测
@Component({...})
class MyComponent {
count = signal(0);
increment() {
this.count.update(v => v + 1); // 自动触发变更检测
}
}
优势: 更小的包体积、更快的启动、更好的 SSR 支持
// 1. 打印当前 Zone
console.log(Zone.current.name);
// 2. 追踪任务调度
Zone.current.fork({
name: 'debug',
onScheduleTask: (delegate, current, target, task) => {
console.log('Task scheduled:', task.source, task.type);
return delegate.scheduleTask(target, task);
},
onInvokeTask: (delegate, current, target, task) => {
console.log('Task invoked:', task.source);
return delegate.invokeTask(target, task);
}
}).run(() => { /* your code */ });
// 3. 检测任务泄漏
setInterval(() => {
console.log('Pending tasks:',
Zone.current._zoneDelegate._taskCounts
);
}, 5000);
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 变更检测不触发 | 代码在 Zone 外执行 | 使用 NgZone.run() |
| 变更检测过于频繁 | 高频事件触发 | runOutsideAngular + 手动触发 |
| 内存泄漏 | 未取消的 EventTask | 确保 removeEventListener |
| 第三方库不工作 | 使用了原生 Promise | 在 Zone 外加载,或 patch |
| 测试超时 | Zone 等待任务完成 | 使用 fakeAsync/flush |
| 方案 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| Zone.js | Monkey Patch | 自动追踪,无需修改代码 | 性能开销,包体积 |
| React Fiber | 手动 yield | 细粒度控制 | 需要开发者配合 |
| Vue 3 | Proxy + Scheduler | 精确追踪 | 需要响应式 API |
| Signal | 依赖图 + 自动传播 | 零 Zone 开销 | 需要学习新 API |
Signal + Zoneless 是 Angular 的演进方向
感谢阅读!
访问 https://atcfu.com/ai-articles/angular-zonejs-async/ 回顾本文