Home Reference Source Test Repository

src/js/util/queue.js

/**
 * Based on Queue.js by Stephen Morley - http://code.stephenmorley.org/
 */

/**
 * Creates a new queue. A queue is a first-in-first-out (FIFO) data structure -
 * items are added to the end of the queue and removed from the front.
 * @class Queue
 */
class Queue {

  /**
   * Constructs a Queue instance.
   * @constructs Queue
   */
  constructor() {
    // initialise the queue and offset
    this.queue = [];
    this.offset = 0;
  }

  /**
   * Getter for size, allows access as a property).
   * @return {number} - Size of the queue.
   */
  get size() {
    return this.queue.length - this.offset;
  }

  /**
   * Clear the queue.
   */
  clear() {
    this.queue = [];
    this.offset = 0;
  }

  /**
   * Enqueues the specified item.
   * @param {*} item - the item to enqueue
   */
  enqueue(item) {
    if (typeof item === 'undefined' || item === null) {
      throw Error('Item must be defined and non-null');
    }
    this.queue.push(item);
  }

  /**
   * Dequeues an item and returns it. If the queue is empty, the value null is returned.
   * @return {?*} - The next item in the queue, or null if the queue is empty.
   */
  dequeue() {
    // if the queue is empty, return immediately
    if (this.queue.length === 0) {
      return null;
    }

    // store the item at the front of the queue
    let item = this.queue[this.offset];

    // increment the offset and remove the free space if necessary
    if (++this.offset * 2 >= this.queue.length) {
      this.queue = this.queue.slice(this.offset);
      this.offset = 0;
    }

    // return the dequeued item
    return item;
  }

  /**
   * Returns the item at the front of the queue (without dequeuing it). If the queue is empty then null is returned.
   * @return {?*} - The next item in the queue, or null if the queue is empty.
   */
  peek() {
    return this.queue.length > 0 ? this.queue[this.offset] : null;
  }

  /**
   * Check if the queue contains the specified item.
   * @param  {*}  item - Item to check.
   * @return {boolean} - Whether or not the item is in the queue.
   * @throws {Error} - Throws an error if checking null or undefined.
   */
  has(item) {
    if (typeof item === 'undefined' || item === null) {
      throw Error('Item must be defined and non-null');
    }
    for (let i = this.offset; i < this.queue.length; i++) {
      if (this.queue[i] === item) {
        return true;
      }
    }
    return false;
  }

  /**
   * Helper function to loop through all items in the queue.
   * @param  {function(item: *): boolean} callback - Callback function called for each item in the queue. If the return value is false, the remaining items are skipped.
   */
  forEach(callback) {
    for (let i = this.offset; i < this.queue.length; i++) {
      if (callback(this.queue[i]) === false) {
        break;
      }
    }
  }

}

export { Queue };
export default Queue;