Fill out ItemQueue implementation

This commit is contained in:
Starbeamrainbowlabs 2022-02-09 01:53:29 +00:00
parent 44d64c09d0
commit 3bdbad150c
Signed by: sbrl
GPG key ID: 1BE5172E637709C2

View file

@ -2,7 +2,15 @@
import { EventEmitter, once } from 'events'; import { EventEmitter, once } from 'events';
import ErrorWrapper from '../core/ErrorWrapper.mjs';
/**
* Implements a simple asynchronous item queue.
* @extends EventEmitter
*/
class ItemQueue extends EventEmitter { class ItemQueue extends EventEmitter {
constructor() { constructor() {
super(); super();
this.items = []; this.items = [];
@ -13,18 +21,42 @@ class ItemQueue extends EventEmitter {
this.emit("push", item); this.emit("push", item);
} }
/**
* Pushes 1 more more items into the queue.
* @param {any} items The items to add to the queue.
* @return {void}
*/
push(...items) { push(...items) {
for(let item of items)
this.__push_single(item);
} }
async pop(time_limit = 0) { /**
if(this.items.length === 0) { * Removes an item from the queue.
} * If no items exist in the queue at the moment, at more time_limit_ms
* milliseconds will elapse before this method returns undefined.
* @param {Number} [time_limit_ms=0] The maximum number of milliseconds to wait before timing out.
* @return {Promise} A Promise that resolves to either the next item in the queue, or undefined if it timed out.
*/
async pop(time_limit_ms = 0) {
if(this.items.length === 0)
return await this.wait_for_item(time_limit_ms);
this.pop() let item = this.items.pop();
this.emit("pop", item);
if(this.items.length === 0)
this.emit("empty");
return item;
} }
wait_for_item(time_limit_ms = 0) { /**
* Waits at most time_limit_ms for the next item to be pushed, before popping and returning it.
* You probably do not want to call this method directly - try the .pop() method instead.
* @param {Number} [time_limit_ms=0] The maximum time to wait for the item to come in before timing out.
* @return {Promise} A Promise that resolves to the next item, or undefined if we timed out waiting.
*/
async wait_for_item(time_limit_ms = 0) {
const ac = new AbortController(); const ac = new AbortController();
let timeout = null; let timeout = null;
if(time_limit_ms > 0) { if(time_limit_ms > 0) {
@ -32,16 +64,56 @@ class ItemQueue extends EventEmitter {
ac.abort(); ac.abort();
}, time_limit_ms); }, time_limit_ms);
} }
let item = await once(this, "push", { signal: ac.signal });
if(timeout !== null)
clearTimeout(timeout);
this.items.splice(this.items.indexOf(item)); try {
return item; let item = await once(this, "push", { signal: ac.signal });
if(timeout !== null)
clearTimeout(timeout);
this.items.splice(this.items.indexOf(item));
return item;
}
catch(error) {
if(timeout !== null)
clearTimeout(timeout);
if(error.name !== "AbortError")
throw new ErrorWrapper(`Error: An unknown error occurred while waiting for the next item.`, error);
return undefined;
}
} }
wait_empty() { /**
* Waits for the queue to be empty.
* @param {Number} [time_limit_ms=0] The maximum number of milliseconds to wait for the queue to be empty before timing out.
* @return {Promise} A Promise that resolves to a bool when the queue is empty (true) or we time out (false).
*/
async wait_empty(time_limit_ms = 0) {
const ac = new AbortController();
let timeout = null;
if(time_limit_ms > 0) {
timeout = setTimeout(() => {
ac.abort();
}, time_limit_ms);
}
try {
await once(this, "empty", { signal: ac.signal });
if(timeout !== null)
clearTimeout(timeout);
return true;
}
catch(error) {
if(timeout !== null)
clearTimeout(timeout);
if(error.name !== "AbortError")
throw new ErrorWrapper(`Error: An unknown error occurred while waiting for the next item.`, error);
return false;
}
} }
} }