import { Injectable , NgZone } from '@angular/core' ; @ Injectable ( ) export class CountdownTimer { private fns : Array < ( ( count : number ) => number | void ) | number > = [ ] ; private commands : Array < ( ) => void > = [ ] ; private nextTime = 0 ; private ing = false ; constructor ( private ngZone ) { } start ( ) { if ( this . ing === true ) { return ; } this . ing = true ; this . nextTime = + new Date ( ) ; this . ngZone . runOutsideAngular ( ( ) => { this . process ( ) ; } ) ; } private process ( ) { while ( this . commands . length ) { this . commands . shift ( ) ! ( ) ; } let diff = + new Date ( ) - this . nextTime ; const count = 1 + Math . floor ( diff / 100 ) ; diff = 100 - ( diff % 100 ) ; this . nextTime += 100 * count ; for ( let i = 0 , len = this . fns . length ; i < len ; i += 2 ) { let frequency = this . fns [ i + 1 ] as number ; if ( 0 === frequency ) { ( this . fns [ i ] as ( count : number ) => void ) ( count ) ; } else { frequency += 2 * count - 1 ; const step = Math . floor ( frequency / 20 ) ; if ( step > 0 ) { ( this . fns [ i ] as ( count : number ) => void ) ( step ) ; } this . fns [ i + 1 ] = ( frequency % 20 ) + 1 ; } } if ( ! this . ing ) { return ; } setTimeout ( ( ) => this . process ( ) , diff ) ; } add ( fn , frequency ) { this . commands . push ( ( ) => { this . fns . push ( fn ) ; this . fns . push ( frequency === 1000 ? 1 : 0 ) ; this . ing = true ; } ) ; return this ; } remove ( fn ) { this . commands . push ( ( ) => { const i = this . fns . indexOf ( fn ) ; if ( i !== - 1 ) { this . fns . splice ( i , 2 ) ; } this . ing = this . fns . length > 0 ; } ) ; return this ; } }