Zone这个模块是用来管理任务异步调用的。
概览
在它的lib源代码目录下有很多文件夹。/lib/Zone.ts存放了zone的核心代码,其他的文件夹主要是对核心代码的拓展,测试以及兼容加强。
Zone的核心代码中主要用Zone,ZoneType,ZoneSpec,ZoneDelegate,Task这几个核心类。头部代码定义了各种接口,最后在底部代码一个变量名为Zone的闭包中,实现这些类,并初始化了许多内容。
Zone
Zone是一种用于拦截和跟踪异步工作的机制, 同时作为暴露出去的全局变量。
在Zone.ts文件中, Zone接口定义在初始化闭包之外,闭包内部有同名为Zone的类的实现。为了不混淆和重负使用命名,Zone初始化闭包都使用了AmbientZone和AmbientZoneDelegate的变量为接口名。
/** @internal */
type AmbientZone = Zone;
/** @internal */
type AmbientZoneDelegate = ZoneDelegate;
属性
parent
Zone和Dom类似,有父子结构,可以通过parent递归调用name
用于标识每个不同的zone
静态方法
- __symbol__
Zone内部很多全局变量的名字都会通过者这个方法加入global['__Zone_symbol_prefix']或'zone_symbol'为前缀,避免全局变量污染。
const symbolPrefix = global['__Zone_symbol_prefix'] || '__zone_symbol__';
function __symbol__(name: string) {
return symbolPrefix + name;
}
- __load_patch
__load_patch 是用来为Zone增强功能补丁的方法,和__symbol__一样,源码还加入了备注忽略tslint的下划线检查。代码头部根据需求检查是否重复加载或者重名,没有的情况下,通过fn(global, Zone, _api)
的方法加载补丁。mark和performanceMeasure都是用来测试性能节点的。
// tslint:disable-next-line:require-internal-with-underscore
static __load_patch(name: string, fn: _PatchFn, ignoreDuplicate = false): void {
if (patches.hasOwnProperty(name)) {
// `checkDuplicate` option is defined from global variable
// so it works for all modules.
// `ignoreDuplicate` can work for the specified module
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, Zone, _api);
performanceMeasure(perfName, perfName);
}
}
- assertZonePatched
检查是否重复加载了Zone, 重复加载会报错
只读静态属性
root
从Zone.current开始,遍历找到顶部Zone。current
返回_currentZoneFrame中的zonecurrentTask
返回_currentTask
方法
- getZoneWith
通过源码,我们可以看到getZoneWith方法会从方法的调用者开发向上递归,直到找到_properties有属性名为key的Zone,并返回Zone或者null(如果没找到)。_properties的定义在ZoneDelegate部分会另外叙述。
public getZoneWith(key: string): AmbientZone|null {
let current: Zone|null = this;
while (current) {
if (current._properties.hasOwnProperty(key)) {
return current;
}
current = current._parent;
}
return null;
}
- get
get方法内部调用了getZoneWith方法来遍历寻找Zone,并返回要查询的属性值。
public get(key: string): any {
const zone: Zone = this.getZoneWith(key) as Zone;
if (zone) return zone._properties[key];
}
- fork
public fork(zoneSpec: ZoneSpec): AmbientZone
fork方法会要求传入一个zoneSpec,然后为当前Zone添加一个child zone。 如果没有传入Zonespec, 会直接报错。zone的fork方法会调用_zoneDelegate的fork方法,后然下面是zoneDelegate的fork代码片段。_forkZS在zoneDelegate初始化的时候,会判断有没有onFork方法,没有的话,会一直向上寻找,确保一定有onFork这个方法。然后就是能onFork就直接调用,不行的话就会new 一个Zone。
onFork后的感叹号有两个作用,一个是闭包方法,还有一个是为方法返回一个true值。
fork(targetZone: Zone, zoneSpec: ZoneSpec): AmbientZone {
return this._forkZS ? this._forkZS.onFork!(this._forkDlgt!, this.zone, targetZone, zoneSpec) :
new Zone(targetZone, zoneSpec);
}
wrap
public wrap<T extends Function>(callback: T, source: string): T
wrap方法传入的callback可以是继承了Function的泛型。source则是用与debug定位。zoneDelegate的intercept方法会封装一层callback,然后返回一个T类型的方法,在该返回方法中通过zone的runGuarded运行callback方法。run
public run<T>(callback: (...args: any[]) => T, applyThis?: any, applyArgs?: any[], source?: string): T
调用zoneDelegate的invoke方法,无谓是否报错都将_currentZoneFrame设置为_currentZoneFrame.parent。runGuarded
public runGuarded<T>(callback: (...args: any[]) => T, applyThis: any = null, applyArgs?: any[], source?: string)
和run 相比, runGuarded多了一个catch调用handleError
catch (error) {
if (this._zoneDelegate.handleError(this, error)) {
throw error;
}
}
- runTask
runTask(task: Task, applyThis?: any, applyArgs?: any): any
通过设置Zone.currentTask, 最终调用this._zoneDelegate.invokeTask来执行task。执行代码前会临时保存当前的_currentTask和_currentZoneFrame,最终在finally代码块中恢复之前的_currentTask和_currentZoneFrame。
const previousTask = _currentTask;
_currentTask = task;
_currentZoneFrame = {parent: _currentZoneFrame, zone: this};
//... other code
_currentZoneFrame = _currentZoneFrame.parent!;
_currentTask = previousTask;
- scheduleMicroTask
- scheduleMacroTask
- scheduleEventTask
这三个Schedule都会调用scheduleTask,不同的是MircoTask没有customCancel的传入
scheduleTask
scheduleTask<T extends Task>(task: T): T
调用this._zoneDelegate.scheduleTaskcancelTask
cancelTask(task: Task): any
调用this._zoneDelegate.cancelTask
ZoneType
ZoneType在头部定义接口以后主要有两个地方会用到,一个是底部代码执行闭包的类型const Zone: ZoneType = (function(global: any)
, 还有一个就是作为Zone增强补丁的参数类型type _PatchFn = (global: Window, Zone: ZoneType, api: _ZonePrivate) => void;
ZoneType的属性和方法,是Zone的静态属性和方法的调用或者增强。
current
currentTask
root
assertZonePatched
assertZonePatched(): void;
__load_patch
\_\_load_patch(name: string, fn: _PatchFn, ignoreDuplicate?: boolean): void;
__symbol__(name: string): string;
__symbol__(name: string): string;
ZoneSpec
ZoneSpec为Zone配置事件
属性
name
和Zone一样,用于Debugproperties
配置Zone可以使用的属性,后期不能增删
事件
onFork
onFork?:(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,zoneSpec: ZoneSpec) => Zone;
在Zone.fork的时候调用onIntercept
onIntercept?:(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, delegate: Function, source: string) => Function;
在Zone.wrap callback的时候调用onInvoke
onInvoke?:(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, delegate: Function,applyThis: any, applyArgs?: any[], source?: string) => any;
在Zone.callback调用的时候调用onHandleError
onHandleError?:(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, error: any) => boolean;
在Zone invoke报错时调用,参考Zone.runGuardedonScheduleTask
onScheduleTask?:(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, task: Task) => Task;
在Zone.scheduleTask时调用onInvokeTask
onInvokeTask?:(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, task: Task, applyThis: any, applyArgs?: any[]) => any;
在Zone.runTask时调用onCancelTask
onCancelTask?:(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, task: Task) => any;
在用Zone.cancelTask时调用onHasTask
onHasTask?:(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, hasTaskState: HasTaskState) => void;
在zone中HasTask变化时调用
ZoneDelegate
ZoneDelegate可以拦截区域操作时的委托
private 变量
_parentDelegate 这是一个内部私有变量, 与Zone类中的parent类似,用于向上遍历,递归调用 。但ZoneDelegate的接口并没有要求_parentDelegate属性,可见一些增加类中,可以使用其他结构,递归调用。
_taskCounts
Zone内部使用_taskCounts对象分别为microTask,macroTask和eventTask计数。私有代理方法
以fork为例,ZoneDelegate分别有三个对象_forkDlgt,_forkZS,_forkCurrZone分别指向fork在ZoneDelegate,ZoneSpec和Zone中的调用者。这点适用于接口中所有的方法。
fork相关私有变量
private _forkDlgt: ZoneDelegate|null;
private _forkZS: ZoneSpec|null;
private _forkCurrZone: Zone|null;
hasTask私有相关变量多了一个_hasTaskDlgtOwner
private _hasTaskDlgt: ZoneDelegate|null;
private _hasTaskDlgtOwner: ZoneDelegate|null;
private _hasTaskZS: ZoneSpec|null;
private _hasTaskCurrZone: Zone|null;
constructor
constructor(zone: Zone, parentDelegate: ZoneDelegate|null, zoneSpec: ZoneSpec|null)
ZoneDelegate的构造函数有大量代码实现代理这一过程。
- fork
没有zoneSpec,以下指针都为null
有onFork时:
_forkZS指向zoneSpec
_forkDlgt指向parentDelegate
_forkCurrZone指向this.zone
不然
_forkZS指向parentDelegate的_forkZS
_forkDlgt指向parentDelegate的_forkDlgt
_forkCurrZone指向parentDelegate的_forkCurrZone
以后指针同理,可以看到其实这地方代码结构高度重复,可以考虑写个help方法
_interceptZS
_invokeZS
_handleErrorZS
_scheduleTaskZS
_invokeTaskZS
_cancelTaskZS
this._forkZS = zoneSpec && (zoneSpec && zoneSpec.onFork ? zoneSpec : parentDelegate!._forkZS);
this._forkDlgt = zoneSpec && (zoneSpec.onFork ? parentDelegate : parentDelegate!._forkDlgt);
this._forkCurrZone = zoneSpec && (zoneSpec.onFork ? this.zone : parentDelegate!._forkCurrZone);
- _hasTaskZS 这里有点不一样了, 有task但没有onScheduleTask,onInvokeTask,onCancelTask的时候,重新指向着三个指针
const zoneSpecHasTask = zoneSpec && zoneSpec.onHasTask;
const parentHasTask = parentDelegate && parentDelegate._hasTaskZS;
if (zoneSpecHasTask || parentHasTask) {
// If we need to report hasTask, than this ZS needs to do ref counting on tasks. In such
// a case all task related interceptors must go through this ZD. We can't short circuit it.
this._hasTaskZS = zoneSpecHasTask ? zoneSpec : DELEGATE_ZS;
this._hasTaskDlgt = parentDelegate;
this._hasTaskDlgtOwner = this;
this._hasTaskCurrZone = zone;
if (!zoneSpec!.onScheduleTask) {
this._scheduleTaskZS = DELEGATE_ZS;
this._scheduleTaskDlgt = parentDelegate!;
this._scheduleTaskCurrZone = this.zone;
}
if (!zoneSpec!.onInvokeTask) {
this._invokeTaskZS = DELEGATE_ZS;
this._invokeTaskDlgt = parentDelegate!;
this._invokeTaskCurrZone = this.zone;
}
if (!zoneSpec!.onCancelTask) {
this._cancelTaskZS = DELEGATE_ZS;
this._cancelTaskDlgt = parentDelegate!;
this._cancelTaskCurrZone = this.zone;
}
}
方法
fork
fork(targetZone: Zone, zoneSpec: ZoneSpec): AmbientZone
如果找到 this._forkZS,那么调用this._forkZS.onFork,不然返回new Zoneintercept
intercept(targetZone: Zone, callback: Function, source: string): Function
如果有this._interceptZS, 那么调用this._interceptZS.onIntercept, 不然直接使用callbackinvoke
invoke(targetZone: Zone, callback: Function, applyThis: any, applyArgs?: any[],source?: string): any
如果有this._invokeZS, 那么调用this._invokeZS.onInvoke, 不然直接使用callbackhandleError
handleError(targetZone: Zone, error: any): boolean
有this._handleErrorZS则调用this._handleErrorZS.onHandleError, 不然返回true
invokeTask
invokeTask(targetZone: Zone, task: Task, applyThis: any, applyArgs?: any[]): any
如果有this._invokeTaskZS, 那么调用this._invokeTaskZS.onInvokeTask, 不然直接使用callbackscheduleTask
scheduleTask(targetZone: Zone, task: Task): Task
首先使用_scheduleTaskZS.onScheduleTask
然后task.scheduleFn(task)cancelTask
cancelTask(targetZone: Zone, task: Task): any
如果有this._cancelTaskZS则调用,不然调用task.cancelFn。都没有则会报错hasTask
hasTask(targetZone: Zone, isEmpty: HasTaskState)
调用this._hasTaskZS.onHasTask, 失败则报错
Task
任务管理
属性
type
包含microTask
,macroTask
,eventTask
TaskState
包含notScheduled
,scheduling
,scheduled
,running
,canceling
,unknown
source
debug使用invoke
调用函数callback
调用完成后的回调函数data
会被传递给scheduleFnrunCount
任务执行的次数,如cancel任务,则会变成-1
方法
scheduleFn
scheduleFn?: (task: Task) => void;
安排任务cancelFn
cancelFn?: (task: Task) => void;
取消以及安排的任务cancelScheduleRequest
cancelScheduleRequest(): void;
取消安排任务的请求, 会调用this._transitionTo(notScheduled, scheduling)
, 如果task不是scheduling则会报错。
只读
- zone
会调用callback,需要在创建Task的时候传入
constructor
constructor(type: T, source: string, callback: Function, options: TaskData|undefined, scheduleFn: ((task: Task) => void)|undefined, cancelFn: ((task: Task) => void)|undefined)
辅助方法
- _transitionTo
_transitionTo(toState: TaskState, fromState1: TaskState, fromState2?: TaskState)
Zone的Task在切换状态的时候,会要求输入fromState,如果fromState不是当前状态就会报错,如果 toState是notScheduled,那么this._zoneDelegates会变成null
闭包内全局方法
- nativeScheduleMicroTask
function nativeScheduleMicroTask(func: Function)
nativeScheduleMicroTask入在当前任务线程结束后立刻到用func方法,首先尝试使用的是Promise.resolve().then(), 没有Promise则会使用SetTimout()。
scheduleMicroTask
function scheduleMicroTask(task?: MicroTask)
首先_numberOfNestedTaskFrames为0和_microTaskQueue队列为空的时候,使用nativeScheduleMicroTask运行drainMicroTaskQueue,最后在_microTaskQueue中添加taskdrainMicroTaskQueue
function drainMicroTaskQueue()
执行_microTaskQueue队列中所有的任务,使用_isDrainingMicrotaskQueue变量保证同一时间,任务只会执行一次。