andywer/threads.js

Typescript workers don't work with ESM

lynxtaa opened this issue ยท 7 comments

Thank you for this library!

Using Typescript Workers with "type": "module" in package.json produces an error:

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /home/alexk/code/threads-ts-node-esm-issue/src/worker.ts
require() of ES modules is not supported.

See reproduction https://github.com/lynxtaa/threads-ts-node-esm-issue

Also here's the example how it works without threads.js using vanilla worker_threads: https://github.com/lynxtaa/threads-ts-node-esm-issue/tree/no-threads

same here

same problem with Vite bundler

I've omitted a threads.js dependency long time ago and wrote a simple helper for creating workers, which works in compiled code and in tests. Hope someone finds it helpful:

// runWorker.ts
import { cpus } from 'os'
import { Worker } from 'worker_threads'

import PQueue from 'p-queue'

const queue = new PQueue({ concurrency: cpus().length })

export function runWorker<T>(filenameWithoutExtension: URL, workerData?: unknown): Promise<T> {
  return queue.add(async () => {
    const worker =
      process.env.NODE_ENV === 'test'
        ? new Worker(new URL(`${filenameWithoutExtension}.ts`), {
            workerData,
            execArgv: ['--loader', 'ts-node/esm/transpile-only'],
          })
        : new Worker(new URL(`${filenameWithoutExtension}.js`), { workerData })

    const result = await new Promise<T>((resolve, reject) => {
      worker.on('message', resolve)
      worker.on('error', reject)
      worker.on('exit', code => {
        if (code !== 0) {
          reject(new Error(`Worker stopped with exit code ${code}`))
        }
      })
    })

    return result
  })
}

// worker.ts
import { workerData, parentPort } from 'worker_threads'

export type WorkerData = number[]
export type WorkerResult = number

const numbers = workerData as WorkerData

const result: WorkerResult = numbers.reduce((prev, curr) => prev + curr, 0)

parentPort!.postMessage(result)

// index.ts
import type { WorkerData, WorkerResult } from './worker.js'
import { runWorker } from './runWorker.js'

const workerData: WorkerData = [1, 2]

const result = await runWorker<WorkerResult>(
  new URL('./worker', import.meta.url),
  workerData,
)

If there's anyone else still having problems with this like I was, I made this to work for what I needed: https://github.com/jackcannon/threads-esm

If there's anyone else still having problems with this like I was, I made this to work for what I needed: https://github.com/jackcannon/threads-esm

I named mine thread-es, just to keep things confusing:
https://github.com/852Kerfunkle/threads-es