As part of FW-1265, the `zone.js` package is made compatible with the TypeScript `--strict` flag. Read more about the strict flag [here](https://www.typescriptlang.org/docs/handbook/compiler-options.html) PR Close #30993
		
			
				
	
	
		
			134 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			134 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| /**
 | |
|  * @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(this: unknown) {
 | |
|       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);
 | |
|         }
 | |
|       });
 | |
| }
 |