Skip to content

Instantly share code, notes, and snippets.

@passbyval
Last active August 17, 2023 02:02
Show Gist options
  • Save passbyval/5097eb5a7a92a11c06350b36443bd6aa to your computer and use it in GitHub Desktop.
Save passbyval/5097eb5a7a92a11c06350b36443bd6aa to your computer and use it in GitHub Desktop.
A web worker hook for React
import { useCallback, useEffect, useRef } from 'react'
import dedent from 'ts-dedent'
export interface IOptions extends Omit<WorkerOptions, 'type'> {
mimeType?: BlobPropertyBag['type']
workerType?: WorkerOptions['type']
}
export type IFunc<R, P = any> = (...args: P[]) => R | Promise<R>
export const useWorker = <T = unknown>(
fn: IFunc<T>,
{
mimeType = 'text/javascript',
workerType = 'classic',
...workerOptions
}: IOptions = {},
) => {
const worker = useRef<Worker>()
const createWorker = () => {
const code = dedent`
onmessage = async ({ data }) => {
const fn = ${fn}
const result = await fn(data)
postMessage(result)
}
`
const blob = new Blob([code], { type: mimeType })
const url = (URL || webkitURL).createObjectURL(blob)
const worker = new Worker(url, { type: workerType, ...workerOptions })
return [worker, url] as const
}
useEffect(() => {
if (!window.Worker) return
const [_worker, url] = createWorker()
worker.current = _worker
return () => {
worker.current!.terminate()
URL.revokeObjectURL(url)
}
}, [])
const workerHandler = useCallback(
async <R = T, P = unknown>(...workerArgs: Parameters<IFunc<R, P>>) => {
return new Promise<R>((resolve, reject) => {
if (!window.Worker) {
;(async () => {
try {
const result = await fn(...workerArgs)
resolve(<PromiseLike<R>>result)
} catch (error) {
reject(error)
}
})()
}
worker.current!.onmessage = async ({ data }: MessageEvent<R>) => resolve(data)
worker.current!.onerror = (error: ErrorEvent) => reject(error.message)
worker.current!.postMessage(workerArgs.at(0))
})
},
[],
)
return [workerHandler] as const
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment