2022-02-01 03:05:27 +00:00
|
|
|
"use strict";
|
|
|
|
|
|
|
|
import { EventEmitter, once } from 'events';
|
|
|
|
|
2022-02-09 01:53:29 +00:00
|
|
|
import ErrorWrapper from '../core/ErrorWrapper.mjs';
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implements a simple asynchronous item queue.
|
|
|
|
* @extends EventEmitter
|
|
|
|
*/
|
2022-02-01 03:05:27 +00:00
|
|
|
class ItemQueue extends EventEmitter {
|
2022-02-09 01:53:29 +00:00
|
|
|
|
2022-02-01 03:05:27 +00:00
|
|
|
constructor() {
|
|
|
|
super();
|
2022-02-09 03:06:52 +00:00
|
|
|
|
2022-02-01 03:05:27 +00:00
|
|
|
this.items = [];
|
|
|
|
}
|
|
|
|
|
2022-02-21 03:10:49 +00:00
|
|
|
__enqueue_single(item) {
|
2022-02-01 03:05:27 +00:00
|
|
|
this.items.push(item);
|
|
|
|
this.emit("push", item);
|
|
|
|
}
|
|
|
|
|
2022-02-09 01:53:29 +00:00
|
|
|
/**
|
|
|
|
* Pushes 1 more more items into the queue.
|
|
|
|
* @param {any} items The items to add to the queue.
|
|
|
|
* @return {void}
|
|
|
|
*/
|
2022-02-21 03:10:49 +00:00
|
|
|
enqueue(...items) {
|
2022-02-09 01:53:29 +00:00
|
|
|
for(let item of items)
|
2022-02-21 03:10:49 +00:00
|
|
|
this.__enqueue_single(item);
|
2022-02-01 03:05:27 +00:00
|
|
|
}
|
|
|
|
|
2022-02-09 01:53:29 +00:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2022-02-21 03:10:49 +00:00
|
|
|
async dequeue(time_limit_ms = 0) {
|
2022-02-09 01:53:29 +00:00
|
|
|
if(this.items.length === 0)
|
|
|
|
return await this.wait_for_item(time_limit_ms);
|
2022-02-01 03:05:27 +00:00
|
|
|
|
2022-02-21 03:10:49 +00:00
|
|
|
let item = this.items.shift();
|
2022-02-09 01:53:29 +00:00
|
|
|
this.emit("pop", item);
|
|
|
|
if(this.items.length === 0)
|
|
|
|
this.emit("empty");
|
|
|
|
|
|
|
|
return item;
|
2022-02-01 03:05:27 +00:00
|
|
|
}
|
|
|
|
|
2022-02-09 01:53:29 +00:00
|
|
|
/**
|
|
|
|
* 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) {
|
2022-02-01 03:05:27 +00:00
|
|
|
const ac = new AbortController();
|
|
|
|
let timeout = null;
|
|
|
|
if(time_limit_ms > 0) {
|
|
|
|
timeout = setTimeout(() => {
|
|
|
|
ac.abort();
|
|
|
|
}, time_limit_ms);
|
|
|
|
}
|
|
|
|
|
2022-02-09 01:53:29 +00:00
|
|
|
try {
|
|
|
|
let item = await once(this, "push", { signal: ac.signal });
|
|
|
|
if(timeout !== null)
|
|
|
|
clearTimeout(timeout);
|
|
|
|
|
|
|
|
this.items.splice(this.items.indexOf(item));
|
2022-02-21 03:10:49 +00:00
|
|
|
return item[0];
|
2022-02-09 01:53:29 +00:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
2022-02-01 03:05:27 +00:00
|
|
|
}
|
|
|
|
|
2022-02-09 01:53:29 +00:00
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
}
|
2022-02-01 03:05:27 +00:00
|
|
|
|
2022-02-09 01:53:29 +00:00
|
|
|
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;
|
|
|
|
}
|
2022-02-01 03:05:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default ItemQueue;
|