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
Worker
instance 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
Worker
thread usingpostMessage
andonmessage
.
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.ts
runs in a separate thread, calculates the sum ofnum1
andnum2
, 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 useimport
andexport
. 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
Worker
script is loaded correctly, enabling processing leveragingWorker
.
Considerations for Messaging and Concurrency
-
Data Copying
When sending and receiving messages between the main thread and the
Worker
thread, 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. -
Transferable
ObjectsSome objects, such as
ArrayBuffer
, are calledTransferable
objects.Transferable
objects 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()
, thebuffer
is transferred to the Worker instead of being copied. -
After this, the
buffer
on the main thread becomes empty (withbyteLength
0) 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
terminate
method.
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
onerror
event.
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.