目录
1.首先看component.ts中schedule 函数,核心代码就是获取director.getScheduler(),并调用schedule方法,把callback等参数传递进去。
2.再看到scheduler.ts类中的schedule方法,只取一些主要代码,下面会分段详细拆解下面的代码:
2.1.根据组件的uuid或id,获取一个HashTimer。
2.1.1此中的_hashForTimers是什么?就是一个v8下的字典模式,主要方法就是Object.create(null),创建一个无原型链的对象,绕过一些检测机制,不需要用hasOwnProperty来判断对象内是否存在某元素,直接用_hashForTimers["anyString"],若不存在该值,则值为undefine
2.1.2.这里的HashTimerEntry类里包含了:
2.2.若无element,就新建一个,并push到一个数组中_arrayForTimers。另存数组里是为方便遍历调用,_hashForTimers是为了方便通过uuid直接取到对象。
2.3.创建timers数组,准备存入callback方法,若callback方法已经存在,只改变调用间隔。
2.4.从缓存池里pop出一个CallbackTimer对象,若无就新建一个,填充数据后push到timers中。
3.加入的全过程看完了,那么怎么触发callback呢?
4.再看到scheduler中的update函数:
5.跳转到 CallbackTimer 类:
6.trigger 和 cancel 函数:
7.看到scheduler 的 unschedule 函数:
public schedule (callback, interval = 0, repeat: number = legacyCC.macro.REPEAT_FOREVER, delay = 0) {
assertID(callback, 1619);
interval = interval || 0;
assertID(interval >= 0, 1620);
repeat = Number.isNaN(repeat) ? legacyCC.macro.REPEAT_FOREVER : repeat;
delay = delay || 0;
const scheduler = legacyCC.director.getScheduler();
// should not use enabledInHierarchy to judge whether paused,
// because enabledInHierarchy is assigned after onEnable.
// Actually, if not yet scheduled, resumeTarget/pauseTarget has no effect on component,
// therefore there is no way to guarantee the paused state other than isTargetPaused.
const paused = scheduler.isTargetPaused(this);
scheduler.schedule(callback, this, interval, repeat, delay, paused);
}
public schedule (callback: (dt?: number) => void, target: ISchedulable, interval: number, repeat?: number, delay?: number, paused?: boolean) {
...
const targetId = target.uuid || target.id;
...
let element = this._hashForTimers[targetId];
if (!element) {
element = HashTimerEntry.get(null, target, 0, null, null, paused);
this._arrayForTimers.push(element);
this._hashForTimers[targetId] = element;
} else if (element.paused !== paused) {
warnID(1511);
}
let timer;
let i;
if (element.timers == null) {
element.timers = [];
} else {
for (i = 0; i < element.timers.length; ++i) {
timer = element.timers[i];
if (timer && callback === timer._callback) {
logID(1507, timer.getInterval(), interval);
timer._interval = interval;
return;
}
}
}
timer = CallbackTimer.get();
timer.initWithCallback(this, callback, target, interval, repeat, delay);
element.timers.push(timer);
if (this._currentTarget === element && this._currentTargetSalvaged) {
this._currentTargetSalvaged = false;
}
}
const targetId = target.uuid || target.id;
let element = this._hashForTimers[targetId];
export function createMap (forceDictMode?: boolean): any {
const map = Object.create(null);
if (forceDictMode) {
const INVALID_IDENTIFIER_1 = '.';
const INVALID_IDENTIFIER_2 = '/';
// assign dummy values on the object
map[INVALID_IDENTIFIER_1] = 1;
map[INVALID_IDENTIFIER_2] = 1;
delete map[INVALID_IDENTIFIER_1];
delete map[INVALID_IDENTIFIER_2];
}
return map;
}
this._hashForTimers = createMap(true);
constructor (timers: any, target: ISchedulable, timerIndex: number, currentTimer: any, currentTimerSalvaged: any, paused: any) {
this.timers = timers;
this.target = target;
this.timerIndex = timerIndex;
this.currentTimer = currentTimer;
this.currentTimerSalvaged = currentTimerSalvaged;
this.paused = paused;
}
if (!element) {
// Is this the 1st element ? Then set the pause level to all the callback_fns of this target
element = HashTimerEntry.get(null, target, 0, null, null, paused);
this._arrayForTimers.push(element);
this._hashForTimers[targetId] = element;
}
if (element.timers == null) {
element.timers = [];
} else {
for (i = 0; i < element.timers.length; ++i) {
timer = element.timers[i];
if (timer && callback === timer._callback) {
logID(1507, timer.getInterval(), interval);
timer._interval = interval;
return;
}
}
}
主要数据有:
这里还有一个put函数,把该回收的对象放入到缓存池中,看到这里的代码可以为我们提供一些编写代码的思路,对于缓存池需要有一个最大池数量和一个锁标记。
class CallbackTimer {
public static get = () => CallbackTimer._timers.pop() || new CallbackTimer()
public static put = (timer: CallbackTimer | any) => {
if (CallbackTimer._timers.length < MAX_POOL_SIZE && !timer._lock) {
timer._scheduler = timer._target = timer._callback = null;
CallbackTimer._timers.push(timer);
}
}
public initWithCallback (scheduler: any, callback: any, target: ISchedulable, seconds: number, repeat: number, delay: number) {
this._lock = false;
this._scheduler = scheduler;
this._target = target;
this._callback = callback;
this._elapsed = -1;
this._interval = seconds;
this._delay = delay;
this._useDelay = (this._delay > 0);
this._repeat = repeat;
this._runForever = (this._repeat === legacyCC.macro.REPEAT_FOREVER);
return true;
}
}
timer = CallbackTimer.get();
timer.initWithCallback(this, callback, target, interval, repeat, delay);
element.timers.push(timer);
回到director类中,能看到在init类中,把 this._scheduler push到 this._systems 中,然后在tick(主循环)中遍历 this._systems 调用 scheduler.update 函数。
export class Director extends EventTarget {
public init () {
...
this.registerSystem(Scheduler.ID, this._scheduler, 200);
...
}
/**
* @en Register a system.
* @zh 注册一个系统。
*/
public registerSystem (name: string, sys: System, priority: number) {
sys.id = name;
sys.priority = priority;
this._systems.push(sys);
this._systems.sort(System.sortByPriority);
}
public tick (dt: number) {
...
for (let i = 0; i < this._systems.length; ++i) {
this._systems[i].update(dt);
}
...
}
}
这里先说一个题外话,在update函数中,还包含了有优先级区分的 scheduleUpdate 函数push进来的调用处理,这里不细说。
遍历 this._arrayForTimers,再遍历 每一个元素中的 timers ,调用 timers[i] 的 updeate 函数,也就是 CallbackTimer 类中的 updeate 函数。
public update (dt) {
...
// Iterate over all the custom selectors
let elt;
const arr = this._arrayForTimers;
for (i = 0; i < arr.length; i++) {
elt = arr[i];
this._currentTarget = elt;
this._currentTargetSalvaged = false;
if (!elt.paused) {
// The 'timers' array may change while inside this loop
for (elt.timerIndex = 0; elt.timerIndex < elt.timers.length; ++(elt.timerIndex)) {
elt.currentTimer = elt.timers[elt.timerIndex];
elt.currentTimerSalvaged = false;
elt.currentTimer.update(dt);
elt.currentTimer = null;
}
}
// only delete currentTarget if no actions were scheduled during the cycle (issue #481)
if (this._currentTargetSalvaged && this._currentTarget.timers.length === 0) {
this._removeHashElement(this._currentTarget);
--i;
}
}
...
}
首先 this._elapsed 加上 dt,再判断是否满足间隔触发时间,延迟时间,循环次数,若满足就调用 this.trigger() 函数,在触发后若不满足继续条件,就调用 this.cancel() 销毁自身。
public update (dt: number) {
if (this._elapsed === -1) {
this._elapsed = 0;
this._timesExecuted = 0;
} else {
this._elapsed += dt;
if (this._runForever && !this._useDelay) { // standard timer usage
if (this._elapsed >= this._interval) {
this.trigger();
this._elapsed = 0;
}
} else { // advanced usage
if (this._useDelay) {
if (this._elapsed >= this._delay) {
this.trigger();
this._elapsed -= this._delay;
this._timesExecuted += 1;
this._useDelay = false;
}
} else if (this._elapsed >= this._interval) {
this.trigger();
this._elapsed = 0;
this._timesExecuted += 1;
}
// @ts-expect-error Notes written for over eslint
if (this._callback && !this._runForever && this._timesExecuted > this._repeat) {
this.cancel();
}
}
}
}
trigger函数就是调用callback函数,调用的方法是 this._callback.call(this._target, this._elapsed) 显式地设置函数的上下文;并且在调用过程中给 _lock 值加了锁,这样在函数调用过程中就不会被 清理掉。
cancel函数就是调用 scheduler 的 unschedule 函数。
public trigger () {
if (this._target && this._callback) {
this._lock = true;
this._callback.call(this._target, this._elapsed);
this._lock = false;
}
}
public cancel () {
// override
this._scheduler.unschedule(this._callback, this._target);
}
通过uuid或id获取元素,遍历其 timers 对象,找到 callback 对应位置,把此对象 put 到缓存池中,等待后续有需要时重新拿出来用。
public unschedule (callback, target: ISchedulable) {
if (!target || !callback) {
return;
}
const targetId = target.uuid || target.id;
if (!targetId) {
errorID(1510);
return;
}
const element = this._hashForTimers[targetId];
if (element) {
const timers = element.timers;
for (let i = 0, li = timers.length; i < li; i++) {
const timer = timers[i];
if (callback === timer._callback) {
if ((timer === element.currentTimer) && (!element.currentTimerSalvaged)) {
element.currentTimerSalvaged = true;
}
timers.splice(i, 1);
CallbackTimer.put(timer);
if (element.timerIndex >= i) {
element.timerIndex--;
}
if (timers.length === 0) {
if (this._currentTarget === element) {
this._currentTargetSalvaged = true;
} else {
this._removeHashElement(element);
}
}
return;
}
}
}
}
如果觉得有帮助请给我点赞并收藏哦~您的支持是我最大的鼓励~