export class DelayedRunner { private isRunning = false ; private isDirty = false ; private pauseDepths : { [ scope ] : number ; } = { } ; private timeoutId : number = 0 ; constructor ( private drainedOption ? ) { } request ( delay ? ) { this . isDirty = true ; if ( ! this . isPaused ( ) ) { this . clearTimeout ( ) ; if ( delay == null ) { this . tryDrain ( ) ; } else { this . timeoutId = setTimeout ( this . tryDrain . bind ( this ) , delay ) as unknown as number ; } } } pause ( scope = '' ) { let { pauseDepths } = this ; pauseDepths [ scope ] = ( pauseDepths [ scope ] || 0 ) + 1 ; this . clearTimeout ( ) ; } resume ( scope = '' , force ? ) { let { pauseDepths } = this ; if ( scope in pauseDepths ) { if ( force ) { delete pauseDepths [ scope ] ; } else { pauseDepths [ scope ] -= 1 ; let depth = pauseDepths [ scope ] ; if ( depth <= 0 ) { delete pauseDepths [ scope ] ; } } this . tryDrain ( ) ; } } isPaused ( ) { return Object . keys ( this . pauseDepths ) . length ; } tryDrain ( ) { if ( ! this . isRunning && ! this . isPaused ( ) ) { this . isRunning = true ; while ( this . isDirty ) { this . isDirty = false ; this . drained ( ) ; } this . isRunning = false ; } } clear ( ) { this . clearTimeout ( ) ; this . isDirty = false ; this . pauseDepths = { } ; } private clearTimeout ( ) { if ( this . timeoutId ) { clearTimeout ( this . timeoutId ) ; this . timeoutId = 0 ; } } protected drained ( ) { if ( this . drainedOption ) { this . drainedOption ( ) ; } } }