DEV Community

Mister Singh
Mister Singh

Posted on • Edited on

How I used web workers to make my react app stop freezing

The Problem:

I have been working on this site – app.sidekik.xyz. It polls the users hard-drive every few seconds and reads their code, detects changes & then rebuilds the UI to interact with that code. As you can imagine that reading large files is heavy work and the app started to become slow when reading a large number of large sized files 🐢.

The why?

In the browser there is only 1 thread, the main thread, which is the only thing taking user inputs and updating the UI. Now, if you run JS code which is eating up too much of its resources, then the app's UI will start to suffer. So yea 😬.

The Solution:

With web workers, we get setup another thread, then offload any amount of work to this thread, thus reducing the load on the main thread and making it not hang 🚀.

Basis Code:

First get npm i workerize-loader. This enables loading any es6 module as a worker and then call the functions it exports. It also supports async/await, which makes it much easier to interact with the worker functions.

Here is my worker file: src/workers/bg.js

import md5 from "md5";
import readJsonObjFromFileHandle from "../helpers/readJsonObjFromFileHandle";

export async function getFileMd5(fileHandle) {
  const fileJsonObj = await readJsonObjFromFileHandle(fileHandle);
  const fileStringObj = JSON.stringify(fileJsonObj);
  const fileMd5 = md5(fileStringObj);
  return fileMd5;
}
Enter fullscreen mode Exit fullscreen mode

Then i load this worker in my app like this:

import { useEffect } from "react";
import worker from "workerize-loader?name=bg!../workers/bg";
import worker from "../workers/bg";

const workerRef = useRef();

useEffect(() => {
  workerRef.current = worker();
  return () => {
    workerRef.current.terminate();
  };
}, []);

Enter fullscreen mode Exit fullscreen mode

Note: Here I am setting up 1 worker to run when the app starts up and then letting it run as long as the app is there. I found this to perform better than creating a then terminating the worker, on a need only basis.

Upon loading the App now, we can see there is a worker.

Image description

Next, we just have to use this worker in my polling function:

const readFiles = () => {
  const fileMd5 = await workerRef.current.getFileMd5(fileHandle);
// rest of application logic here to use the files md5
}
setInterval(readFiles, 2000);
Enter fullscreen mode Exit fullscreen mode

The result:

Now the app is no longer slow, as the main thread is no longer running code to read files & check their md5's.

🎉🥳🎉🥳

Top comments (0)