@augment-vir/common
Version:
A collection of augments, helpers types, functions, and classes for any JavaScript environment.
92 lines (91 loc) • 2.98 kB
JavaScript
import { check } from '@augment-vir/assert';
import { DeferredPromise } from '@augment-vir/core';
import { defineTypedCustomEvent, ListenTarget } from 'typed-event-target';
/**
* The event emitted from a {@link PromiseQueue} instance whenever the queue is updated (an item is
* resolved or an item is added).
*
* @category Promise : Util
* @category Package : @augment-vir/common
* @package [`@augment-vir/common`](https://www.npmjs.com/package/@augment-vir/common)
*/
export class PromiseQueueUpdateEvent extends defineTypedCustomEvent()('promise-queue-update') {
}
/**
* A queue that manages its items with promises.
*
* @category Promise
* @category Package : @augment-vir/common
* @package [`@augment-vir/common`](https://www.npmjs.com/package/@augment-vir/common)
*/
export class PromiseQueue extends ListenTarget {
queue = [];
currentlyAwaiting = undefined;
/** The current size of the queue. */
get size() {
return this.queue.length;
}
/**
* Add an item to the queue.
*
* @returns A promise that resolves at the same time as the added item.
*/
add(item) {
const newItem = {
original: item,
wrapper: new DeferredPromise(),
};
this.queue.push(newItem);
this.dispatch(new PromiseQueueUpdateEvent({
detail: {
queueSize: this.size,
addedItem: newItem,
},
}));
this.triggerNextQueueItem();
return newItem.wrapper.promise;
}
/** Handles a queue item finishing, whether it be a rejection or a resolution. */
handleItemSettle({ rejection, resolution, }) {
const item = this.currentlyAwaiting;
if (!item) {
throw new Error(`Cannot handle queue item settle without a currently awaited queue item.`);
}
this.queue.splice(0, 1);
this.dispatch(new PromiseQueueUpdateEvent({
detail: {
queueSize: this.size,
finishedItem: item,
},
}));
if (rejection) {
item.wrapper.reject(rejection);
}
else {
item.wrapper.resolve(resolution);
}
this.currentlyAwaiting = undefined;
this.triggerNextQueueItem();
return item;
}
/**
* Tries to trigger the next queue item, if there is one.
*
* @returns Whether a new queue item was triggered or not. `true` if it was, otherwise `false`.
*/
triggerNextQueueItem() {
if (this.currentlyAwaiting || !check.isLengthAtLeast(this.queue, 1)) {
return false;
}
const item = this.queue[0];
this.currentlyAwaiting = item;
item.original()
.then((result) => {
this.handleItemSettle({ resolution: result });
})
.catch((error) => {
this.handleItemSettle({ rejection: error });
});
return true;
}
}