type Resolver < T > = ( value ? : T ) => void ; class QueueEntry < T > { data : T ; next ? : QueueEntry < T > ; constructor ( data ) { this . data = data ; } } /**
 * A linked queue implementation.
 */ export class Queue < T > { head ? : QueueEntry < T > ; tail ? : QueueEntry < T > ; param data enqueue ( data ) { const newNode = new QueueEntry < T > ( data ) ; if ( this . tail ) { this . tail . next = newNode ; } this . tail = newNode ; if ( ! this . head ) { this . head = this . tail ; } } returns { T } throws dequeue ( ) { if ( this . isEmpty ( ) ) { throw new Error ( 'Cannot dequeue. Queue is empty' ) ; } const node = this . head ! . data ; this . head = this . head ! . next ; return node ; } returns { boolean } isEmpty ( ) { return this . head == null ; } } /**
 * The AsyncBlockingQueue implements a queue with an asynchronous programming model. Items can
 * be added to the Queue as usual. When dequeing, a Promise is returned.
 *
 * The promise will resolve instantly if the Queue is not empty. If the Queue is empty, the Promise
 * will be resolved when a new item is added to the queue.
 */ export class AsyncBlockingQueue < T > { private promiseQueue : Queue < Promise < T > > = new Queue < Promise < T > > ( ) ; private resolverQueue : Queue < Resolver < T > > = new Queue < Resolver < T > > ( ) ; private add ( ) { const promise = new Promise < T > ( resolve => { this . resolverQueue . enqueue ( resolve ) ; } ) ; this . promiseQueue . enqueue ( promise ) ; } param data enqueue ( data ) { if ( this . resolverQueue . isEmpty ( ) ) { this . add ( ) ; } const resolve = this . resolverQueue . dequeue ( ) ; resolve ( data ) ; } returns { Promise < T > } async dequeue ( ) { if ( this . promiseQueue . isEmpty ( ) ) { this . add ( ) ; } return this . promiseQueue . dequeue ( ) ; } hasPendingPromises ( ) { return ! this . promiseQueue . isEmpty ( ) ; } hasPendingResolvers ( ) { return ! this . resolverQueue . isEmpty ( ) ; } }