function WorkerHandler(script, _options) {
  var me = this;
  var options = _options || {};

  this.script = script || getDefaultWorker();
  this.worker = setupWorker(this.script, options);
  this.debugPort = options.debugPort;
  this.forkOpts = options.forkOpts;
  this.forkArgs = options.forkArgs;
  this.workerOpts = options.workerOpts;
  this.workerThreadOpts = options.workerThreadOpts
  this.workerTerminateTimeout = options.workerTerminateTimeout;

  // The ready message is only sent if the worker.add method is called (And the default script is not used)
  if (!script) {
    this.worker.ready = true;
  }

  // queue for requests that are received before the worker is ready
  this.requestQueue = [];

  this.worker.on("stdout", function (data) {
    handleEmittedStdPayload(me, {"stdout": data.toString()})
  })
  this.worker.on("stderr", function (data) {
    handleEmittedStdPayload(me, {"stderr": data.toString()})
  })

  this.worker.on('message', function (response) {
    if (me.terminated) {
      return;
    }
    if (typeof response === 'string' && response === 'ready') {
      me.worker.ready = true;
      dispatchQueuedRequests();
    } else {
      // find the task from the processing queue, and run the tasks callback
      var id = response.id;
      var task = me.processing[id];
      if (task !== undefined) {
        if (response.isEvent) {
          if (task.options && typeof task.options.on === 'function') {
            task.options.on(response.payload);
          }
        } else {
          // remove the task from the queue
          delete me.processing[id];

          // test if we need to terminate
          if (me.terminating === true) {
            // complete worker termination if all tasks are finished
            me.terminate();
          }

          // resolve the task's promise
          if (response.error) {
            task.resolver.reject(objectToError(response.error));
          }
          else {
            task.resolver.resolve(response.result);
          }
        }
      } else {
        // if the task is not the current, it might be tracked for cleanup
        var task = me.tracking[id];
        if (task !== undefined) {
          if (response.isEvent) {
            if (task.options && typeof task.options.on === 'function') {
              task.options.on(response.payload);
            }
          }
        } 
      }

      if (response.method === CLEANUP_METHOD_ID) {
        var trackedTask = me.tracking[response.id];
        if (trackedTask !== undefined) {
          if (response.error) {
            clearTimeout(trackedTask.timeoutId);
            trackedTask.resolver.reject(objectToError(response.error))
          } else {
            me.tracking && clearTimeout(trackedTask.timeoutId);
            trackedTask.resolver.resolve(trackedTask.result);            
          }
        }
        delete me.tracking[id];
      }
    }
  });

  // reject all running tasks on worker error
  function onError(error) {
    me.terminated = true;

    for (var id in me.processing) {
      if (me.processing[id] !== undefined) {
        me.processing[id].resolver.reject(error);
      }
    }
    
    me.processing = Object.create(null);
  }

  // send all queued requests to worker
  function dispatchQueuedRequests()
  {
    for(const request of me.requestQueue.splice(0)) {
      me.worker.send(request.message, request.transfer);
    }
  }

  var worker = this.worker;
  // listen for worker messages error and exit
  this.worker.on('error', onError);
  this.worker.on('exit', function (exitCode, signalCode) {
    var message = 'Workerpool Worker terminated Unexpectedly\n';

    message += '    exitCode: `' + exitCode + '`\n';
    message += '    signalCode: `' + signalCode + '`\n';

    message += '    workerpool.script: `' +  me.script + '`\n';
    message += '    spawnArgs: `' +  worker.spawnargs + '`\n';
    message += '    spawnfile: `' + worker.spawnfile + '`\n'

    message += '    stdout: `' + worker.stdout + '`\n'
    message += '    stderr: `' + worker.stderr + '`\n'

    onError(new Error(message));
  });

  this.processing = Object.create(null); // queue with tasks currently in progress
  this.tracking = Object.create(null); // queue with tasks being monitored for cleanup status
  this.terminating = false;
  this.terminated = false;
  this.cleaning = false;
  this.terminationHandler = null;
  this.lastId = 0;
}
A WorkerHandler controls a single worker. This worker can be a child process
on node.js or a WebWorker in a browser environment.
@param {String} [script] If no script is provided, a default worker with a
                         function run will be created.
@param {import('./types.js').WorkerPoolOptions} [_options] See docs
@constructor