import Logger from '../logger/Logger' ; import LogLevel from '../logger/LogLevel' ; import AsyncScheduler from '../scheduler/AsyncScheduler' ; import Task from './Task' ; /**
 * A task that wraps another task and ensures it is run only once,
 * regardless of how many times `run` is called.
 *
 * This allows you to implement a kind of barrier synchronization.
 */ export default class OnceTask implements Task { private ongoing : Promise < void > | undefined ; private promise : Promise < void > | undefined ; private cancelPromise : ( ( e : Error ) => void ) | undefined ; private canceled = false ; constructor ( private logger , private task , private dependencies ? ) { } name ( ) { return `${ this . task . name ( ) } (once)` ; } cancel ( ) { if ( this . canceled ) { return ; } if ( this . dependencies ) { for ( const dep of this . dependencies ) { dep . cancel ( ) ; } } this . logger . info ( `Canceling ${ this . name ( ) }` ) ; AsyncScheduler . nextTick ( ( ) => this . task . cancel ( ) ) ; this . canceled = true ; if ( this . cancelPromise ) { AsyncScheduler . nextTick ( ( ) => this . cancelPromise ( new Error ( `canceling ${ this . name ( ) }` ) ) ) ; } } logDependencies ( ) { if ( this . logger . getLogLevel ( ) > LogLevel . INFO ) { return ; } if ( ! this . dependencies ) { return ; } const names = this . dependencies . filter ( d => d ) . map ( d => d . name ( ) ) . join ( ', ' ) ; this . logger . info ( `${ this . task . name ( ) } waiting for dependencies: ${ names }` ) ; } run ( ) { if ( this . promise ) { return this . promise ; } const dependencies = this . dependencies ? Promise . all ( this . dependencies . map ( d => d ?. run ( ) ) ) : Promise . resolve ( ) ; this . logDependencies ( ) ; this . ongoing = dependencies . then ( ( ) => this . task . run ( ) ) ; return ( this . promise = new Promise ( ( resolve , reject ) => { this . cancelPromise = reject ; this . ongoing . then ( resolve ) . catch ( reject ) ; } ) ) ; } setParent ( parentTask ) { this . task . setParent ( parentTask ) ; } }