Web Worker in TypeScript
This article explains Web Workers in TypeScript.
You can learn about the concept of Web Workers and various usage tips with examples.
YouTube Video
Worker in TypeScript
In TypeScript, a Worker is a mechanism for performing processing in the background, separate from the main thread, leveraging the JavaScript Web Workers API. This allows heavy computations and asynchronous tasks to be executed without affecting the operation of the user interface.
A Worker operates in parallel with the main thread (UI thread) and can exchange data between threads via messages. Even in TypeScript, you can use Worker while writing type-safe code.
Basic Usage of Worker
-
Creating a Worker
Create a
Workerinstance and execute the specified script. Normally, the script is defined in a separate file. -
Message Exchange
Send and receive messages between the main thread and the
Workerthread usingpostMessageandonmessage.
Example: Basic Worker Implementation
- worker.ts: Script for Worker
1// worker.ts
2self.onmessage = (event) => {
3 const data = event.data;
4 const result = data.num1 + data.num2;
5 self.postMessage(result); // Return the result to the main thread
6};- main.ts: Script to use Worker in Main Thread
1// main.ts
2const worker = new Worker(
3 new URL('./worker.ts', import.meta.url),
4 { type: 'module' }
5);
6
7worker.onmessage = (event) => {
8 console.log("Result from worker:", event.data); // Receive message from the worker
9};
10
11worker.postMessage({ num1: 10, num2: 20 }); // Send message to the worker
- In this example,
worker.tsruns in a separate thread, calculates the sum ofnum1andnum2, and returns it to the main thread. The main thread receives the result and outputs it to the console. - When you specify
type: 'module', the Worker script is interpreted as an ES module, allowing you to useimportandexport. This allows you to handle module structures without using the traditionalimportScripts().
Points to Consider When Using Workers in TypeScript
Adding Type Definitions
In TypeScript, define data types to ensure type-safe interaction during message sending and receiving.
1// Define data types
2interface WorkerData {
3 num1: number;
4 num2: number;
5}
6
7interface WorkerResult {
8 result: number;
9}
10
11// worker.ts
12self.onmessage = (event: MessageEvent<WorkerData>) => {
13 const data = event.data;
14 const result = data.num1 + data.num2;
15 const message: WorkerResult = { result };
16 self.postMessage(message); // Send the result in a type-safe manner
17};
18
19// main.ts
20const worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' });
21
22// Type the event from the worker
23worker.onmessage = (event: MessageEvent<WorkerResult>) => {
24 console.log("Result from worker:", event.data); // event.data is number
25};
26
27// Send typed data to the worker
28const message: WorkerData = { num1: 10, num2: 20 };
29worker.postMessage(message);- By specifying the type parameter of
MessageEvent, you can clearly define the type of data to receive. This allows data to be exchanged with type safety.
Setting Up Webpack or Vite
When using Worker in TypeScript, bundlers like Webpack or Vite may be required. By using these tools, you can properly bundle the Worker script and make it available from the main thread.
For example, when using Vite, use import.meta.url to correctly import the Worker.
1const worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' });- This ensures that the bundled
Workerscript is loaded correctly, enabling processing leveragingWorker.
Considerations for Messaging and Concurrency
-
Data Copying
When sending and receiving messages between the main thread and the
Workerthread, data is copied. When dealing with complex data such as objects, efficiency needs to be considered. Frequently exchanging large amounts of data can degrade performance. -
TransferableObjectsSome objects, such as
ArrayBuffer, are calledTransferableobjects.Transferableobjects can be transferred to the Worker instead of being copied during messaging. This allows you to avoid the overhead of data copying.
1const worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' });
2
3const buffer = new ArrayBuffer(1024);
4worker.postMessage(buffer, [buffer]); // Transfer ownership to the Worker
5
6console.log(buffer.byteLength); // 0 (ownership moved)
-
By passing
[buffer]as the second argument toworker.postMessage(), thebufferis transferred to the Worker instead of being copied. -
After this, the
bufferon the main thread becomes empty (withbyteLength0) and can only be used by the Worker.
Terminating a Worker
The Worker should be terminated after use to minimize memory consumption. You can terminate a Worker by using the terminate method.
1const worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' });
2// ...
3
4worker.terminate(); // Terminate the Worker
- This code terminates the Worker by using the
terminatemethod.
Exception Handling in Worker
If an error occurs within a Worker, you can handle the error using the onerror event.
1const worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' });
2
3worker.onerror = (error) => {
4 console.error("Error in Worker:", error.message);
5};- This code catches and handles errors that occur inside the Worker using the
onerrorevent.
Summary
By using Worker in TypeScript, you can execute heavy tasks in the background while keeping the main thread smooth. By utilizing type definitions, message exchanges can also be done in a type-safe manner. By paying attention to data exchange and thread management, you can achieve performance improvements and efficient concurrency.
You can follow along with the above article using Visual Studio Code on our YouTube channel. Please also check out the YouTube channel.