Skip to content

nishio/tutorial-webworker-with-react

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Tutorial of WebWorker + react-create-app --typescript

Create React App

$ npx create-react-app tutorial-webworker-with-react --typescript

Install worker-loader

$ cd tutorial-webworker-with-react/
$ npm install worker-loader --save-dev

Add a heavy task

export const heavyTask = (target: string) => {
  console.log("hello,", target);
  const startTime = Date.now();
  while (Date.now() - startTime < 3000) {}
  console.log("bye,", target);
  return 42;
};

See the heavy task blocks UI

See full changes on commit: 774345f

const runOnUIThread = (e: any) => {
  status = "running...";
  heavyTask("UIThread");
  status = "finished";
};
...
<button onClick={runOnUIThread}>on UI Thread</button>

Add webworker

import { heavyTask } from "./common";

onmessage = (e: any) => {
  const result = heavyTask("webworker");
  // @ts-ignore
  postMessage(result);
};

Unless @ts-ignore, I saw following transpile error:

function postMessage(message: any, targetOrigin: string, transfer?: Transferable[] | undefined): void
Expected 2-3 arguments, but got 1.ts(2554)
lib.dom.d.ts(19636, 44): An argument for 'targetOrigin' was not provided.

However, adding second argument such as postMessage(result, "*") causes following runtime error:

Uncaught TypeError: Failed to execute 'postMessage' on 'DedicatedWorkerGlobalScope':
No function was found that matched the signature provided.

If you hate @ts-ignore, see another solution: "use cast instead of ts-ignore" 1d3c2a4

Add button to call webworker

See full changes on commit: 0f5c964

// eslint-disable-next-line import/no-webpack-loader-syntax
import Worker from "worker-loader!./webworker";

export const runOnWebWorker = (e: any) => {
  status = "running...";
  const worker = new Worker();
  worker.postMessage("heavyTask");
  worker.onmessage = function(event: any) {
    status = "finished";
  };
};
...
<button onClick={runOnWebWorker}>on WebWorker</button>

NOTICE: You don't need to modifiy webpack.config.js if you use inlined webpack-loader-syntax as above. I saw several people are confusing about that.

In development environment npm run start, it works. But you may see following error on IDE (for example VSCode): Cannot find module 'worker-loader!./webworker'.

The error also occurs in release environment npm run build.

To fix the error, you need to add custom type definition.

Add custom type definition

See full changes on commit: a9c0c03

declare module "worker-loader!*" {
  class WebpackWorker extends Worker {
    constructor();
  }
  export default WebpackWorker;
}

If you didn't specify typeRoots on tsconfig yet, you also need it. See the commit.

Appendix

Concurrency

In this tutorial, I create new worker each call. If you push button before the previous task finishes, the new task runs concurrently.

export const runOnWebWorker = (e: any) => {
  status = "running...";
  const worker = new Worker();
  worker.postMessage("heavyTask");
  worker.onmessage = function(event: any) {
    status = "finished";
  };
};

If we re-use the worker object like below, the new message waits in queue until the previous task finishes.

const worker = new Worker();
worker.onmessage = function(event: any) {
  status = "finished";
};

export const runOnWebWorker = (e: any) => {
  status = "running...";
  worker.postMessage("heavyTask");
};

You can not pass functions to workers

Following code produce an error below.

const v = { inc: (x: number) => x + 1 };
worker.postMessage(v);
DataCloneError: Failed to execute 'postMessage' on 'Worker': x => x + 1 could not be cloned.

In case of a class instance with methods like below:

class Vec2D {
  x: number;
  y: number;
  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
  }
  scale(s: number) {
    this.x *= s;
    this.y *= s;
    return this;
  }
}

the postMessage success, but the methods are not passed to the worker.

// App.tsx
const v = new Vec2D(1, 2);
v.scale(2);
worker.postMessage(v); // OK
// webworker.ts
e.data.scale(3); // NG
// Uncaught TypeError: e.data.scale is not a function
//  at onmessage (webworker.ts:4)

Commit: 588193c

About

tutorial of webworker + react-create-app --typescript

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published