type Callback < T , R > = [ T ] extends [ undefined ] ? ( data ? : T ) => R : ( data : T ) => R ; type TaskResult < R > = { success : false ; } | { success : true ; result : R ; } ; class EventChannelTask < T = undefined , R = void > { public constructor ( public taskCallback , public originalCallback , public ttl ) { } } let batchedRun = callback => callback ( ) ; export const setBatchedUpdates = ( batchedUpdates ) => { batchedRun = batchedUpdates ; } ; example export class EventChannel < T = undefined , R = void > { protected tasks : Array < EventChannelTask < T , R > > = [ ] ; protected enqueueTask ( callback , times , filter ) { const channel = this ; const task = new EventChannelTask < T , R > ( function taskCallback ( data ) { if ( this . ttl <= 0 ) { channel . off ( this . originalCallback ) ; return { success : false } ; } if ( filter ( data ) ) { this . ttl -= 1 ; if ( this . ttl <= 0 ) { channel . off ( this . originalCallback ) ; } return { success : true , result : batchedRun ( ( ) => callback ( data ) ) } ; } return { success : false } ; } , callback , times ) ; this . tasks . push ( task ) ; return ( ) => this . off ( callback ) ; } protected emitTask ( data ) { return batchedRun ( ( ) => { const results = [ ] ; const forkedTasks = [ ... this . tasks ] ; for ( let index = 0 ; index < forkedTasks . length ; index += 1 ) { const task = forkedTasks [ index ] ; const result = task . taskCallback ( data ) ; if ( result . success ) { results . push ( result . result ) ; } } return results ; } ) ; } public on ( callback ) { return this . enqueueTask ( callback , Infinity , ( ) => true ) ; } public once ( callback ) { return this . enqueueTask ( callback , 1 , ( ) => true ) ; } /**
     * use `match` to filter events and run `callback` only once if matched
     */ public filteredOnce ( filter , callback ) { return this . enqueueTask ( callback , 1 , filter ) ; } public emit : Callback < T , Array < R > > = data => this . emitTask ( data ) ; public off ( callback ) { const pos = this . tasks . findIndex ( listener => listener . originalCallback === callback ) ; if ( pos !== - 1 ) { this . tasks [ pos ] . ttl = 0 ; this . tasks . splice ( pos , 1 ) ; } } public offAll ( ) { for ( const task of this . tasks ) { task . ttl = 0 ; } this . tasks = [ ] ; } public listenerCount ( ) { return this . tasks . length ; } } export class CachedEventChannel < T = undefined , R = void > extends EventChannel < T , R > { public constructor ( ) { super ( ) ; } private isCached = false ; private cachedData ? : T ; public override emit : Callback < T , Array < R > > = data => { this . isCached = true ; this . cachedData = data ; return this . emitTask ( data ) ; } ; public override on ( callback ) { const cancel = super . on ( callback ) ; if ( this . isCached ) { batchedRun ( ( ) => callback ( this . cachedData ! ) ) ; } return cancel ; } public override once ( callback ) { const cancel = super . once ( callback ) ; if ( this . isCached ) { batchedRun ( ( ) => callback ( this . cachedData ! ) ) ; cancel ( ) ; } return cancel ; } public override filteredOnce ( filter , callback ) { const cancel = super . filteredOnce ( filter , callback ) ; if ( this . isCached && filter ( this . cachedData ! ) ) { batchedRun ( ( ) => callback ( this . cachedData ! ) ) ; cancel ( ) ; } return cancel ; } }