import { IAbortAblePromise , ABORTED , IAbortAblePromiseBase } from 'lineupengine' ; export { ABORTED } from 'lineupengine' ; internal export interface IPoorManIdleDeadline { didTimeout : boolean ; timeRemaining ( ) : number ; } internal export interface IPoorManIdleCallback { requestIdleCallback ( callback , options ? ) : number ; clearIdleCallback ( callbackId ) : void ; } internal export interface ITask < T > { /**
     * external task id
     */ id : string ; /**
     * iterator for the incremental task, return null for another round
     */ it : Iterator < T | PromiseLike < T > | null > ; /**
     * function to the resolve the resulting promise
     */ resolve ( r ) : void ; /**
     * the resulting promise
     */ result : PromiseLike < T | symbol > ; /**
     * aborts this task
     */ abort : ( ) => void ; isAborted : boolean ; } internal export const ANOTHER_ROUND = { value : null , done : false , } ; internal export function oneShotIterator < T > ( calc ) { return { next : ( ) => ( { done : true , value : calc ( ) } ) , } ; } function thenFactory < T > ( wrappee , abort , isAborted ) { function then < TResult1 = T | symbol , TResult2 = never > ( onfulfilled ? , onrejected ? ) { const r = wrappee . then ( onfulfilled , onrejected ) ; return { then : thenFactory ( r , abort , isAborted ) as any , abort , isAborted , } ; } return then ; } internal export default class TaskScheduler { private tasks : ITask < any > [ ] = [ ] ; private taskId = - 1 ; private runTasks = ( deadline ) => { while ( this . tasks . length > 0 && ( deadline . didTimeout || deadline . timeRemaining ( ) > 0 ) ) { const task = this . tasks . shift ( ) ! ; let r = task . it . next ( ) ; while ( ! r . done && ( deadline . didTimeout || deadline . timeRemaining ( ) > 0 ) ) { r = task . it . next ( ) ; } if ( r . done ) { requestAnimationFrame ( ( ) => task . resolve ( r . value ) ) ; } else { this . tasks . unshift ( task ) ; } } this . taskId = - 1 ; this . reSchedule ( ) ; } ; private reSchedule ( ) { if ( this . tasks . length === 0 || this . taskId > - 1 ) { return ; } const ww = self as any as IPoorManIdleCallback ; if ( ww . requestIdleCallback ) { this . taskId = ww . requestIdleCallback ( this . runTasks ) ; } else { this . taskId = setTimeout ( this . runTasks , 1 ) as unknown as number ; } } param id param it pushMulti < T > ( id , it , abortAble = true ) { const abort = ( ) => { const index = this . tasks . findIndex ( ( d ) => d . id === id ) ; if ( index < 0 ) { return ; } const task = this . tasks [ index ] ; this . tasks . splice ( index , 1 ) ; task . isAborted = true ; task . resolve ( ABORTED ) ; } ; { const index = this . tasks . findIndex ( ( d ) => d . id === id ) ; if ( index >= 0 ) { const task = this . tasks [ index ] ; task . abort ( ) ; } } let resolve ; const p = new Promise < T | symbol > ( ( r ) => { resolve = r ; } ) ; const task = { id , it , result : p , abort , isAborted : false , resolve : resolve ! , } ; const isAborted = ( ) => task . isAborted ; this . tasks . push ( task ) ; this . reSchedule ( ) ; const abortOrDummy = abortAble ? abort : ( ) => undefined ; const isAbortedOrDummy = abortAble ? isAborted : ( ) => false ; return { then : thenFactory ( p , abortOrDummy , isAbortedOrDummy ) , abort : abortOrDummy , isAborted : isAbortedOrDummy , } ; } param id param calc push < T > ( id , calc ) { return this . pushMulti ( id , oneShotIterator ( calc ) ) ; } param id abort ( id ) { const index = this . tasks . findIndex ( ( d ) => d . id === id ) ; if ( index < 0 ) { return false ; } const task = this . tasks [ index ] ; task . abort ( ) ; return true ; } abortAll ( filter ) { const abort = this . tasks . filter ( filter ) ; if ( abort . length === 0 ) { return ; } this . tasks = this . tasks . filter ( ( d ) => ! filter ( d ) ) ; for ( const task of abort ) { task . resolve ( ABORTED ) ; task . abort ( ) ; } } clear ( ) { if ( this . taskId === - 1 ) { return ; } const ww = self as any as IPoorManIdleCallback ; if ( ww . requestIdleCallback ) { ww . clearIdleCallback ( this . taskId ) ; } else { clearTimeout ( this . taskId ) ; } this . taskId = - 1 ; this . tasks . splice ( 0 , this . tasks . length ) . forEach ( ( d ) => { d . resolve ( ABORTED ) ; d . abort ( ) ; } ) ; } }