core/WebWorker.js

import { v4 as createUUID } from '../../node_modules/uuid/dist/esm-browser/index.js';

/**
 * A light wrapper around a Web Worker that makes sending/receiving messages with the worker easier.
 * @memberof Core
 */
export default class WebWorker {

  /**
   * Create a new Web Worker
   * @param {String} url The URL of the worker's JavaScript file.
   */
  constructor(url) {
    this.worker         = new Worker(url);
    this.worker.onerror = console.error;
  }

  /**
   * Post a message to the web worker and await the response.
   * @param   {String}          operation The operation for the worker to perform.
   * @param   {*}               [data]    The data to pass to the worker to operate on.
   * @returns {Promise<Object>} Resolves to the result of the operation.
   */
  post(operation, data) {
    return new Promise((resolve, reject) => {

      const operationID = createUUID();

      const listener = ({ data: result }) => {
        if (result.operationID === operationID) {
          this.worker.removeEventListener(`message`, listener);
          if (result.error) reject(result.error);
          resolve(result.data);
        }
      };

      this.worker.addEventListener(`message`, listener);
      this.worker.postMessage({ data, operation, operationID });

    });
  }

  /**
   * Trigger a web worker operation and subscribe a callback to its stream of emitted events.
   * @param {String}     operation      The operation for the worker to perform.
   * @param {*|Function} dataOrCallback The data to pass to the worker to operate on, or the callback function to call if no data needs to be passed.
   * @param {Function}   [callback]
   */
  stream(operation, dataOrCallback, callback) {

    const cb   = typeof dataOrCallback === `function` ? dataOrCallback : callback;
    const data = typeof dataOrCallback === `function` ? undefined : dataOrCallback;

    const operationID = createUUID();

    const listener = ({ data: result }) => {
      if (result.operationID === operationID) {

        if (result.data) {
          cb(result.data);
        }

        if (result.done) {
          this.worker.removeEventListener(`message`, listener);
        }

      }
    };

    this.worker.addEventListener(`message`, listener);
    this.worker.postMessage({ data, operation, operationID });

  }

  /**
   * Terminate the web worker.
   */
  terminate() {
    this.worker.terminate();
  }

}