UNPKG

3d-tiles-renderer

Version:

https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/specification

219 lines (131 loc) 3.28 kB
export class PriorityQueueItemRemovedError extends Error { constructor() { super( 'PriorityQueue: Item removed' ); this.name = 'PriorityQueueItemRemovedError'; } } export class PriorityQueue { // returns whether tasks are queued or actively running get running() { return this.items.length !== 0 || this.currJobs !== 0; } constructor() { // options this.maxJobs = 6; this.items = []; this.callbacks = new Map(); this.currJobs = 0; this.scheduled = false; this.autoUpdate = true; this.priorityCallback = null; // Customizable scheduling callback. Default using requestAnimationFrame() this.schedulingCallback = func => { requestAnimationFrame( func ); }; this._runjobs = () => { this.scheduled = false; this.tryRunJobs(); }; } sort() { const priorityCallback = this.priorityCallback; const items = this.items; if ( priorityCallback !== null ) { items.sort( priorityCallback ); } } has( item ) { return this.callbacks.has( item ); } add( item, callback ) { const data = { callback, reject: null, resolve: null, promise: null, }; data.promise = new Promise( ( resolve, reject ) => { const items = this.items; const callbacks = this.callbacks; data.resolve = resolve; data.reject = reject; items.unshift( item ); callbacks.set( item, data ); if ( this.autoUpdate ) { this.scheduleJobRun(); } } ); return data.promise; } remove( item ) { const items = this.items; const callbacks = this.callbacks; const index = items.indexOf( item ); if ( index !== - 1 ) { // reject the promise to ensure there are no dangling promises - add a // catch here to handle the case where the promise was never used anywhere // else. const info = callbacks.get( item ); info.promise.catch( err => { if ( ! ( err instanceof PriorityQueueItemRemovedError ) ) { throw err; } } ); info.reject( new PriorityQueueItemRemovedError() ); items.splice( index, 1 ); callbacks.delete( item ); } } removeByFilter( filter ) { const { items } = this; for ( let i = 0; i < items.length; i ++ ) { const item = items[ i ]; if ( filter( item ) ) { this.remove( item ); i --; } } } tryRunJobs() { this.sort(); const items = this.items; const callbacks = this.callbacks; const maxJobs = this.maxJobs; let iterated = 0; const completedCallback = () => { this.currJobs --; if ( this.autoUpdate ) { this.scheduleJobRun(); } }; while ( maxJobs > this.currJobs && items.length > 0 && iterated < maxJobs ) { this.currJobs ++; iterated ++; const item = items.pop(); const { callback, resolve, reject } = callbacks.get( item ); callbacks.delete( item ); let result; try { result = callback( item ); } catch ( err ) { reject( err ); completedCallback(); } if ( result instanceof Promise ) { result .then( resolve ) .catch( reject ) .finally( completedCallback ); } else { resolve( result ); completedCallback(); } } } scheduleJobRun() { if ( ! this.scheduled ) { this.schedulingCallback( this._runjobs ); this.scheduled = true; } } }