HEX
Server: Apache/2.4.52 (Ubuntu)
System: Linux ip-172-31-4-197 6.8.0-1036-aws #38~22.04.1-Ubuntu SMP Fri Aug 22 15:44:33 UTC 2025 x86_64
User: ubuntu (1000)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: /var/www/bots/enerbot/node_modules/queue-promise/dist/index.js.flow
// @flow
import EventEmitter from "events";

const State = {
  IDLE: 0,
  RUNNING: 1,
  STOPPED: 2,
};

type Options = {
  concurrent: number,
  interval: number,
  start: boolean,
};

/**
 * A small and simple library for promise-based queues. It will execute enqueued
 * functions concurrently at a specified speed. When a task is being resolved or
 * rejected, an event is emitted.
 *
 * @example
 *    const queue = new Queue({
 *      concurrent: 1,
 *      interval: 2000
 *    });
 *
 *    queue.on("resolve", data => console.log(data));
 *    queue.on("reject", error => console.error(error));
 *
 *    queue.enqueue(asyncTaskA);
 *    queue.enqueue([asyncTaskB, asyncTaskC]);
 *
 * @class   Queue
 * @extends EventEmitter
 */
export default class Queue extends EventEmitter {
  /**
   * A collection to store unresolved tasks. We use a Map here because V8 uses a
   * variant of hash tables that generally have O(1) complexity for retrieval
   * and lookup.
   *
   * @see     https://codereview.chromium.org/220293002/
   * @type    {Map}
   * @access  private
   */
  tasks: Map<number, Function> = new Map();

  /**
   * @type    {number}  Used to generate a unique id for each task
   * @access  private
   */
  uniqueId: number = 0;

  /**
   * @type    {number}
   * @access  private
   */
  lastRan: number = 0;

  /**
   * @type    {TimeoutID}
   * @access  private
   */
  timeoutId: TimeoutID;

  /**
   * @type    {number}  Amount of tasks currently handled by the queue
   * @access  private
   */
  currentlyHandled: number = 0;

  /**
   * @type    {State}
   * @access  public
   */
  state: $Values<typeof State> = State.IDLE;

  /**
   * @type    {Object}  options
   * @type    {number}  options.concurrent  How many tasks should be executed in parallel
   * @type    {number}  options.interval    How often should new tasks be executed (in ms)
   * @type    {boolean} options.start       Whether it should automatically execute new tasks as soon as they are added
   * @access  public
   */
  options: Options = {
    concurrent: 5,
    interval: 500,
    start: true,
  };

  /**
   * Initializes a new queue instance with provided options.
   *
   * @param   {Object}  options
   * @param   {number}  options.concurrent  How many tasks should be executed in parallel
   * @param   {number}  options.interval    How often should new tasks be executed (in ms)
   * @param   {boolean} options.start       Whether it should automatically execute new tasks as soon as they are added
   * @return  {Queue}
   */
  constructor(options: Options = {}) {
    super();

    this.options = { ...this.options, ...options };
    this.options.interval = parseInt(this.options.interval, 10);
    this.options.concurrent = parseInt(this.options.concurrent, 10);
  }

  /**
   * Starts the queue if it has not been started yet.
   *
   * @emits   start
   * @return  {void}
   * @access  public
   */
  start(): void {
    if (this.state !== State.RUNNING && !this.isEmpty) {
      this.state = State.RUNNING;
      this.emit("start");

      (async () => {
        while (this.shouldRun) {
          await this.dequeue();
        }
      })();
    }
  }

  /**
   * Forces the queue to stop. New tasks will not be executed automatically even
   * if `options.start` was set to `true`.
   *
   * @emits   stop
   * @return  {void}
   * @access  public
   */
  stop(): void {
    clearTimeout(this.timeoutId);

    this.state = State.STOPPED;
    this.emit("stop");
  }

  /**
   * Goes to the next request and stops the loop if there are no requests left.
   *
   * @emits   end
   * @return  {void}
   * @access  private
   */
  finalize(): void {
    this.currentlyHandled -= 1;

    if (this.currentlyHandled === 0 && this.isEmpty) {
      this.stop();

      // Finalize doesn't force queue to stop as `Queue.stop()` does. Therefore,
      // new tasks should be still resolved automatically if `options.start` was
      // set to `true` (see `Queue.enqueue`):
      this.state = State.IDLE;

      this.emit("end");
    }
  }

  /**
   * Executes _n_ concurrent (based od `options.concurrent`) promises from the
   * queue.
   *
   * @return  {Promise<any>}
   * @emits   resolve
   * @emits   reject
   * @emits   dequeue
   * @access  private
   */
  async execute(): Promise<*> {
    const promises = [];

    this.tasks.forEach((promise, id) => {
      // Maximum amount of parallel tasks:
      if (this.currentlyHandled < this.options.concurrent) {
        this.currentlyHandled++;
        this.tasks.delete(id);

        promises.push(
          Promise.resolve(promise())
            .then((value) => {
              this.emit("resolve", value);
              return value;
            })
            .catch((error) => {
              this.emit("reject", error);
              return error;
            })
            .finally(() => {
              this.emit("dequeue");
              this.finalize();
            })
        );
      }
    });

    // Note: Promise.all will reject if any of the concurrent promises fail,
    // regardless if they are all finished yet! This is why we are emitting
    // events per task (and not per batch of tasks with respect to
    // `concurrent`):
    const output = await Promise.all(promises);

    return this.options.concurrent === 1 ? output[0] : output;
  }

  /**
   * Executes _n_ concurrent (based od `options.concurrent`) promises from the
   * queue.
   *
   * @return  {Promise<any>}
   * @emits   resolve
   * @emits   reject
   * @emits   dequeue
   * @access  public
   */
  dequeue(): Promise<*> {
    const { interval } = this.options;

    return new Promise<*>((resolve, reject) => {
      const timeout = Math.max(0, interval - (Date.now() - this.lastRan));

      clearTimeout(this.timeoutId);
      this.timeoutId = setTimeout(() => {
        this.lastRan = Date.now();
        this.execute().then(resolve);
      }, timeout);
    });
  }

  /**
   * Adds tasks to the queue.
   *
   * @param   {Function|Array}  tasks     Tasks to add to the queue
   * @throws  {Error}                     When task is not a function
   * @return  {void}
   * @access  public
   */
  enqueue(tasks: Function | Array<Function>): void {
    if (Array.isArray(tasks)) {
      tasks.map((task) => this.enqueue(task));
      return;
    }

    if (typeof tasks !== "function") {
      throw new Error(`You must provide a function, not ${typeof tasks}.`);
    }

    this.uniqueId = (this.uniqueId + 1) % Number.MAX_SAFE_INTEGER;
    this.tasks.set(this.uniqueId, tasks);

    // Start the queue if the queue should resolve new tasks automatically and
    // hasn't been forced to stop:
    if (this.options.start && this.state !== State.STOPPED) {
      this.start();
    }
  }

  /**
   * @see     enqueue
   * @access  public
   */
  add(tasks: Function | Array<Function>): void {
    this.enqueue(tasks);
  }

  /**
   * Removes all tasks from the queue.
   *
   * @return  {void}
   * @access  public
   */
  clear(): void {
    this.tasks.clear();
  }

  /**
   * Size of the queue.
   *
   * @type    {number}
   * @access  public
   */
  get size(): number {
    return this.tasks.size;
  }

  /**
   * Checks whether the queue is empty, i.e. there's no tasks.
   *
   * @type    {boolean}
   * @access  public
   */
  get isEmpty(): boolean {
    return this.size === 0;
  }

  /**
   * Checks whether the queue is not empty and not stopped.
   *
   * @type    {boolean}
   * @access  public
   */
  get shouldRun(): boolean {
    return !this.isEmpty && this.state !== State.STOPPED;
  }
}