
committed by
Kara Erickson

parent
7b3bcc23af
commit
5eb7426216
133
packages/zone.js/lib/common/timers.ts
Normal file
133
packages/zone.js/lib/common/timers.ts
Normal file
@ -0,0 +1,133 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
/**
|
||||
* @fileoverview
|
||||
* @suppress {missingRequire}
|
||||
*/
|
||||
|
||||
import {patchMethod, scheduleMacroTaskWithCurrentZone, zoneSymbol} from './utils';
|
||||
|
||||
const taskSymbol = zoneSymbol('zoneTask');
|
||||
|
||||
interface TimerOptions extends TaskData {
|
||||
handleId?: number;
|
||||
args: any[];
|
||||
}
|
||||
|
||||
export function patchTimer(window: any, setName: string, cancelName: string, nameSuffix: string) {
|
||||
let setNative: Function|null = null;
|
||||
let clearNative: Function|null = null;
|
||||
setName += nameSuffix;
|
||||
cancelName += nameSuffix;
|
||||
|
||||
const tasksByHandleId: {[id: number]: Task} = {};
|
||||
|
||||
function scheduleTask(task: Task) {
|
||||
const data = <TimerOptions>task.data;
|
||||
function timer() {
|
||||
try {
|
||||
task.invoke.apply(this, arguments);
|
||||
} finally {
|
||||
// issue-934, task will be cancelled
|
||||
// even it is a periodic task such as
|
||||
// setInterval
|
||||
if (!(task.data && task.data.isPeriodic)) {
|
||||
if (typeof data.handleId === 'number') {
|
||||
// in non-nodejs env, we remove timerId
|
||||
// from local cache
|
||||
delete tasksByHandleId[data.handleId];
|
||||
} else if (data.handleId) {
|
||||
// Node returns complex objects as handleIds
|
||||
// we remove task reference from timer object
|
||||
(data.handleId as any)[taskSymbol] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
data.args[0] = timer;
|
||||
data.handleId = setNative !.apply(window, data.args);
|
||||
return task;
|
||||
}
|
||||
|
||||
function clearTask(task: Task) { return clearNative !((<TimerOptions>task.data).handleId); }
|
||||
|
||||
setNative =
|
||||
patchMethod(window, setName, (delegate: Function) => function(self: any, args: any[]) {
|
||||
if (typeof args[0] === 'function') {
|
||||
const options: TimerOptions = {
|
||||
isPeriodic: nameSuffix === 'Interval',
|
||||
delay: (nameSuffix === 'Timeout' || nameSuffix === 'Interval') ? args[1] || 0 :
|
||||
undefined,
|
||||
args: args
|
||||
};
|
||||
const task =
|
||||
scheduleMacroTaskWithCurrentZone(setName, args[0], options, scheduleTask, clearTask);
|
||||
if (!task) {
|
||||
return task;
|
||||
}
|
||||
// Node.js must additionally support the ref and unref functions.
|
||||
const handle: any = (<TimerOptions>task.data).handleId;
|
||||
if (typeof handle === 'number') {
|
||||
// for non nodejs env, we save handleId: task
|
||||
// mapping in local cache for clearTimeout
|
||||
tasksByHandleId[handle] = task;
|
||||
} else if (handle) {
|
||||
// for nodejs env, we save task
|
||||
// reference in timerId Object for clearTimeout
|
||||
handle[taskSymbol] = task;
|
||||
}
|
||||
|
||||
// check whether handle is null, because some polyfill or browser
|
||||
// may return undefined from setTimeout/setInterval/setImmediate/requestAnimationFrame
|
||||
if (handle && handle.ref && handle.unref && typeof handle.ref === 'function' &&
|
||||
typeof handle.unref === 'function') {
|
||||
(<any>task).ref = (<any>handle).ref.bind(handle);
|
||||
(<any>task).unref = (<any>handle).unref.bind(handle);
|
||||
}
|
||||
if (typeof handle === 'number' || handle) {
|
||||
return handle;
|
||||
}
|
||||
return task;
|
||||
} else {
|
||||
// cause an error by calling it directly.
|
||||
return delegate.apply(window, args);
|
||||
}
|
||||
});
|
||||
|
||||
clearNative =
|
||||
patchMethod(window, cancelName, (delegate: Function) => function(self: any, args: any[]) {
|
||||
const id = args[0];
|
||||
let task: Task;
|
||||
if (typeof id === 'number') {
|
||||
// non nodejs env.
|
||||
task = tasksByHandleId[id];
|
||||
} else {
|
||||
// nodejs env.
|
||||
task = id && id[taskSymbol];
|
||||
// other environments.
|
||||
if (!task) {
|
||||
task = id;
|
||||
}
|
||||
}
|
||||
if (task && typeof task.type === 'string') {
|
||||
if (task.state !== 'notScheduled' &&
|
||||
(task.cancelFn && task.data !.isPeriodic || task.runCount === 0)) {
|
||||
if (typeof id === 'number') {
|
||||
delete tasksByHandleId[id];
|
||||
} else if (id) {
|
||||
id[taskSymbol] = null;
|
||||
}
|
||||
// Do not cancel already canceled functions
|
||||
task.zone.cancelTask(task);
|
||||
}
|
||||
} else {
|
||||
// cause an error by calling it directly.
|
||||
delegate.apply(window, args);
|
||||
}
|
||||
});
|
||||
}
|
Reference in New Issue
Block a user