vercel / next.js

The React Framework
https://nextjs.org
MIT License
124.84k stars 26.65k forks source link

WebWorkers - Worker is not defined when calling new Worker() #30170

Open polisen opened 2 years ago

polisen commented 2 years ago

What version of Next.js are you using?

11.1.2

What version of Node.js are you using?

14.0.0

What browser are you using?

Chrome

What operating system are you using?

macOS

How are you deploying your application?

other platform

Describe the Bug

I'm using next with typescript and trying to load a webworker.

For some reason - when I'm trying to initialize the webworker when I'm creating a hook - next tells me that Worker is not defined.

I'm using comlink but I suspect it's an issue with Next since comlink isn't in the mix at the point of error.

You can find the problematic line in below code snippet useWorker.hooks.ts in the function makeWorkerApiAndCleanup

const worker = new Worker(url);

useWorker.hooks.ts

import { wrap, releaseProxy } from "comlink";
import { useEffect, useState, useMemo } from "react";

function makeWorkerApiAndCleanup() {
  const url = new URL("./useWorker.worker", import.meta.url)
  const worker = new Worker(url);
  const workerApi = wrap<import("./useWorker.worker").WorkerType>(worker);

  const cleanup = () => {
    workerApi[releaseProxy]();
    worker.terminate();
  };

  const workerApiAndCleanup = { workerApi, cleanup };

  return workerApiAndCleanup;
}

function useWorker() {
  const workerApiAndCleanup = useMemo(() => makeWorkerApiAndCleanup(), []);

  useEffect(() => {
    const { cleanup } = workerApiAndCleanup;
    return () => {
      cleanup();
    };
  }, [workerApiAndCleanup]);

  return workerApiAndCleanup;
}

function useImplementation() {
  const [data, setData] = useState({});

  const { workerApi } = useWorker();

  useEffect(() => {
    workerApi.workerFunction();
    setData({});
  }, [workerApi]);

  return data;
}

export default useImplementation;

useWorker.worker.ts

import { expose } from 'comlink';
import { workerFunction } from './functions';

const worker = {
  workerFunction,
};

export type WorkerType = typeof worker;

expose(worker);

useWorker/index.ts

import useWorker from './useWorker.hooks';

export { useWorker };

Expected Behavior

Well that the Window() constructor to be defined in any context.

To Reproduce

Here's a minimal repo:

https://github.com/polisen/browser-file-bundler

franky47 commented 2 years ago

I just got this error when upgrading from 12.0.9 to 12.1.0.

Calling code (runs on the main thread):

import type { AnalysisWorkerInterface } from 'client/workers/analysis/main'
import { wrap } from 'comlink'
import { nanoid } from 'nanoid'

export const analysisWorker = wrap<AnalysisWorkerInterface>(
  new Worker(
    new URL(
      /* webpackChunkName: "analysis.worker" */
      'client/workers/analysis/main',
      import.meta.url
    ),
    {
      // Using nanoid here to detect multiple instances running in parallel
      name:
        process.env.NODE_ENV === 'production'
          ? 'analysis'
          : `analysis (${nanoid()})`
    }
  )
)
DevAndrewGeorge commented 2 years ago

Can confirm that this seems to be a bug introduced since 12.1.0.

mhoff commented 2 years ago

It seems I'm also affected by this issue. Is there a workaround to use Worker in the meantime?

franky47 commented 1 year ago

Chiming back after running some experiments: the issue is SSR. The Worker object is not available when rendering on the server, but works fine when running in the browser.

So this is a matter of making sure we're importing code that calls new Worker only on the client, either: