| 41 | * are created inside a for loop). |
| 42 | */ |
| 43 | export class TimerScheduler implements OnDestroy { |
| 44 | // Indicates whether current callbacks are being invoked. |
| 45 | executingCallbacks = false; |
| 46 | |
| 47 | // Currently scheduled `setTimeout` id. |
| 48 | timeoutId: number | null = null; |
| 49 | |
| 50 | // When currently scheduled timer would fire. |
| 51 | invokeTimerAt: number | null = null; |
| 52 | |
| 53 | // List of callbacks to be invoked. |
| 54 | // For each callback we also store a timestamp on when the callback |
| 55 | // should be invoked. We store timestamps and callback functions |
| 56 | // in a flat array to avoid creating new objects for each entry. |
| 57 | // [timestamp1, callback1, timestamp2, callback2, ...] |
| 58 | current: Array<number | VoidFunction> = []; |
| 59 | |
| 60 | // List of callbacks collected while invoking current set of callbacks. |
| 61 | // Those callbacks are added to the "current" queue at the end of |
| 62 | // the current callback invocation. The shape of this list is the same |
| 63 | // as the shape of the `current` list. |
| 64 | deferred: Array<number | VoidFunction> = []; |
| 65 | |
| 66 | add(delay: number, callback: VoidFunction, ngZone: NgZone) { |
| 67 | const target = this.executingCallbacks ? this.deferred : this.current; |
| 68 | this.addToQueue(target, Date.now() + delay, callback); |
| 69 | this.scheduleTimer(ngZone); |
| 70 | } |
| 71 | |
| 72 | remove(callback: VoidFunction) { |
| 73 | const {current, deferred} = this; |
| 74 | const callbackIndex = this.removeFromQueue(current, callback); |
| 75 | if (callbackIndex === -1) { |
| 76 | // Try cleaning up deferred queue only in case |
| 77 | // we didn't find a callback in the "current" queue. |
| 78 | this.removeFromQueue(deferred, callback); |
| 79 | } |
| 80 | // If the last callback was removed and there is a pending timeout - cancel it. |
| 81 | if (current.length === 0 && deferred.length === 0) { |
| 82 | this.clearTimeout(); |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | private addToQueue( |
| 87 | target: Array<number | VoidFunction>, |
| 88 | invokeAt: number, |
| 89 | callback: VoidFunction, |
| 90 | ) { |
| 91 | let insertAtIndex = target.length; |
| 92 | for (let i = 0; i < target.length; i += 2) { |
| 93 | const invokeQueuedCallbackAt = target[i] as number; |
| 94 | if (invokeQueuedCallbackAt > invokeAt) { |
| 95 | // We've reached a first timer that is scheduled |
| 96 | // for a later time than what we are trying to insert. |
| 97 | // This is the location at which we need to insert, |
| 98 | // no need to iterate further. |
| 99 | insertAtIndex = i; |
| 100 | break; |
nothing calls this directly
no test coverage detected
searching dependent graphs…