function defineWorkerModule(options) {
  if ((!options || typeof options.init !== 'function') && !_allowInitAsString) {
    throw new Error('requires `options.init` function')
  }
  let {dependencies, init, getTransferables, workerId} = options

  const onMainThread = defineMainThreadModule(options)

  if (workerId == null) {
    workerId = '#default'
  }
  const id = `workerModule${++_workerModuleId}`
  const name = options.name || id
  let registrationPromise = null

  dependencies = dependencies && dependencies.map(dep => {
    // Wrap raw functions as worker modules with no dependencies
    if (typeof dep === 'function' && !dep.workerModuleData) {
      _allowInitAsString = true
      dep = defineWorkerModule({
        workerId,
        name: `<${name}> function dependency: ${dep.name}`,
        init: `function(){return (\n${stringifyFunction(dep)}\n)}`
      })
      _allowInitAsString = false
    }
    // Grab postable data for worker modules
    if (dep && dep.workerModuleData) {
      dep = dep.workerModuleData
    }
    return dep
  })

  function moduleFunc(...args) {
    if (!supportsWorkers()) {
      return onMainThread(...args)
    }

    // Register this module if needed
    if (!registrationPromise) {
      registrationPromise = callWorker(workerId,'registerModule', moduleFunc.workerModuleData)
      const unregister = () => {
        registrationPromise = null
        registeredModules[workerId].delete(unregister)
      }
      ;(registeredModules[workerId] || (registeredModules[workerId] = new Set())).add(unregister)
    }

    // Invoke the module, returning a promise
    return registrationPromise.then(({isCallable}) => {
      if (isCallable) {
        return callWorker(workerId,'callModule', {id, args})
      } else {
        throw new Error('Worker module function was called but `init` did not return a callable function')
      }
    })
  }
  moduleFunc.workerModuleData = {
    isWorkerModule: true,
    id,
    name,
    dependencies,
    init: stringifyFunction(init),
    getTransferables: getTransferables && stringifyFunction(getTransferables)
  }

  moduleFunc.onMainThread = onMainThread;

  return moduleFunc
}
Define a module of code that will be executed with a web worker. This provides a simple
interface for moving chunks of logic off the main thread, and managing their dependencies
among one another.

@param {object} options
@param {function} options.init
@param {array} [options.dependencies]
@param {function} [options.getTransferables]
@param {string} [options.name]
@param {string} [options.workerId]
@return {function(...[*]): {then}}