Introduction
Web Workers are a way to run JavaScript in the background, separate from the main execution thread. This can be useful for offloading tasks that might block the main thread, such as loading large data sets or performing expensive calculations.
Few examples of how Web Workers might be used:
Loading large data sets: Web Workers can be used to load large data sets asynchronously, without blocking the main thread. This can improve the performance of an application and make it feel more responsive.
Performing expensive calculations: Web Workers can be used to perform expensive calculations, such as training machine learning models or performing complex math, without blocking the main thread.
Offloading image processing tasks: Web Workers can be used to process images, such as resizing or applying filters, without blocking the main thread.
Running simulations or animations: Web Workers can be used to run simulations or animations that might be too resource-intensive to run on the main thread.
There are several types of Web Workers:
Inline Workers
An inline worker is a script that is included directly in the HTML page as a script element. Inline workers are useful for small scripts that do not need to be loaded from an external file.
Here is an example of an inline worker:
<script>
// Register a callback to process messages from the parent
self.onmessage = event => {
// Do some computation with the data from the parent
const result = event.data * 2;
// Send the result back to the parent
self.postMessage(result);
};
</script>
External Workers
An external worker is a script that is loaded from an external file using the Worker constructor. External workers are useful for larger scripts that need to be separated from the main execution thread.
Here is an example of an external worker:
// worker.js
// Register a callback to process messages from the parent
self.onmessage = event => {
// Do some computation with the data from the parent
const result = event.data * 2;
// Send the result back to the parent
self.postMessage(result);
};
// App.tsx
import * as React from 'react';
const App: React.FC = () => {
// Create the Worker object and pass it the URL of the worker script
const workerRef = React.useRef(new Worker('./worker.js'));
// Get the worker object from the ref
const worker = workerRef.current;
// Send some data to the worker
worker.postMessage(10);
// Register a callback to process the result from the worker
worker.onmessage = event => {
console.log(event.data); // 20
};
return <div>Hello World!</div>;
};
export default App;
Shared Workers
A shared worker is a worker that can be shared by multiple pages and can communicate with all of them. Shared workers are useful for cases where multiple pages need to share data or communicate with each other.
Here is an example of a shared worker:
// worker.js
// Register a callback to process messages from the parent
self.onmessage = event => {
// Do some computation with the data from the parent
const result = event.data * 2;
// Send the result back to the parent
self.postMessage(result);
};
import * as React from 'react';
const App: React.FC = () => {
// Create the SharedWorker object and pass it the URL of the worker script
const workerRef= React.useRef(new SharedWorker('./worker.js'));
// Get the worker object from the ref
const worker = workerRef.current;
// Send some data to the worker
worker.port.postMessage(10);
// Register a callback to process the result from the worker
worker.port.onmessage = event => {
console.log(event.data); // 20
};
return <div>Hello World!</div>;
};
export default App;
App Demo
We will demonstrate downloading an Excel file using an API and a Web Worker:
- Node.js API to retrieve data for the Excel file
import Express from "express"
import dotenv from "dotenv"
import cors from "cors"
dotenv.config()
const PORT = process.env.PORT
const app : Express.Application = Express()
app.use(cors({
origin: 'http://localhost:5173'
}));
const data = [
{ name: 'John', age: 30 },
{ name: 'Jane', age: 25 }
];
app.get('/api/data', (req, res) => {
res.json(data);
});
app.listen(PORT, () => {
console.log(`Server is listening on ${PORT}`)
})
This API has a single endpoint, /api/data
, which returns a JSON array of data. You can modify this endpoint to retrieve the data from a database or other source as needed.
- We will now use this API in the Web Worker, using the Axios API to process the returned data as needed.
import * as React from 'react';
import axios from 'axios';
import { useEffect, useState } from 'react';
const App: React.FC = () => {
const [url, setUrl] = useState('');
useEffect(() => {
// Make an API call to retrieve the data
axios.get('http://localhost:8080/api/data')
.then(response => {
const data = response.data;
// Create the Worker object and pass it the URL of the worker script
const worker = new Worker('src/worker.js');
// Send the data to the worker
worker.postMessage(data);
// Register a callback to process messages from the worker
worker.onmessage = event => {
// Create a Blob object from the Excel data returned by the worker
const blob = new Blob([event.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
// Create a URL for the Blob object
const objectUrl = URL.createObjectURL(blob);
setUrl(objectUrl);
};
})
.catch(error => {
console.error(error);
});
}, []);
return (
<div>
<a href={url} download="data.xlsx">Download Excel</a>
</div>
);
};
export default App;
//worker.js
importScripts('../node_modules/xlsx/dist/xlsx.full.min.js'); /* imports xlsx */
// Register a callback to process messages from the parent
self.onmessage = event => {
// Retrieve the data from the message
const data = event.data;
// Create an Excel file from the data using SheetJS or XLSX-populate
const workbook = self.XLSX.utils.book_new();
const sheet = self.XLSX.utils.json_to_sheet(data);
self.XLSX.utils.book_append_sheet(workbook, sheet);
const excelData = self.XLSX.write(workbook, { type: 'array', bookType: 'xlsx' });
// Send the Excel data back to the parent
self.postMessage(excelData);
};
This code uses the useEffect
hook to make an API call to retrieve the data and create the Web Worker when the component is mounted. It then sends the data to the worker using the postMessage
method and registers a callback to process the Excel data returned by the worker using the onmessage
method. Finally, it creates a Blob object and a URL for the Blob and renders a link element that allows the user to download the Excel file.
To use the XLSX
library in your web worker, you will need to import it in your web worker script. You can do this using the importScripts()
function, which allows you to load and execute external scripts in your web worker.
Conclusion
Web workers are a useful tool for offloading complex or time-consuming tasks to a separate thread, allowing the main thread to remain responsive and improve the overall performance of your application. In this article, we learned how to use Web Workers in a React application, including creating and communicating with a worker and using it to perform a long-running task. We also saw how to use the SheetJS library to create an Excel file from data received from an API call, and how to use the XLSX-populate library to populate an Excel file with data. By following the steps in this guide, you can easily incorporate Web Workers into your React application to improve its performance and user experience.
Happy coding! ✨
If you have any questions, You can reach out to me on: