“JavaScript is single-threaded”, Js developers have always thought so. But today we are going to introduce multi-threaded programming in JavaScript, you heard me right, I am talking about Multi-Threaded Programming - Web Workers .

I. Preface

Web Workers is a feature of the browser (the host environment), not a standard feature of JavaScript, so what does it do?

Suppose you click a button on a page and it triggers the execution of a series of intensive tasks that take tens of seconds or even minutes to complete, then the UI will be stuck, resulting in a Page Unresponsive error. You definitely don’t want this to happen, and it can lead to a very bad user experience.

You want a way to let the main thread continue to render the UI, while the processing of intensive tasks to another child thread to take charge, and when the child thread is finished, the main thread can get the results of the child thread and react. This is where Web Workers comes in.

You can create a Web worker in the main thread, give some time-consuming tasks to the Web worker, and when the Web worker finishes processing the tasks, it will return the results to the main thread. This will not lead to Page Unresponsive situation.

The Web Workers mechanism differs from the multithreading in C++ and C# in that the child threads (Web workers) cannot share memory with other threads (main threads). This is an advantage, shared memory means shared scope and resources. If memory is shared, then you will encounter all the problems that other multi-threaded languages (C++, C#, etc.) have to face, such as cooperative or preemptive locking mechanisms (mutex, etc.), and that would be too much of a problem.

Once a Worker thread is created successfully, it will always run, so it should be closed after it is used, otherwise it will take up too many resources.

Web Worker has the following points to note when using it.

(1) Homologation restriction

The script file assigned to the worker thread must be the same source as the script file of the main thread.

(2) DOM restriction

The global object where the worker thread is located is not the same as the main thread, so it cannot read the DOM object of the web page where the main thread is located, and it cannot use the document, window, parent objects. However, the Worker thread can read the navigator object and the location object.

(3) Communication links

The worker thread and the main thread are not in the same context, and they cannot communicate directly, but must do so via messages.

(4) Scripting restrictions

The worker thread cannot execute the alert() method and the confirm() method, but can use the XMLHttpRequest object to make AJAX requests.

(5) File restrictions

The Worker thread cannot read local files, i.e. it cannot open the local file system (file://), and the scripts it loads must come from the network.

II. Usage

1. Main thread

Create a new Worker thread inside the main thread.

1
var worker = new Worker('worker.js');

The argument to the Worker() constructor is a script file, which is the execution code of the Worker thread.

The main thread communicates with the Worker via message, calling the worker.postMessage() method to send a message to the Worker. The Worker, upon receiving the message from the main thread, can process the task according to the message content (how to process the task according to the message will be explained later).

1
worker.postMessage({data: "Web worker tutorial"}); 

The argument to postMessage() can be any type of data, including binary.

The main thread can receive messages from child threads by listening to the message event through the worker and getting the data through event.data, similar to how other events are handled.

There are two ways to do this, onmessage() or addEventListener().

(1) onmessage()

1
2
3
4
5
6
7
worker.onmessage = receivedWorkerMessage;

function receivedWorkerMessage(event) {
    var message = event.data; // 消息内容

    ...
}

(2)addEventListener()

1
2
3
4
5
6
7
worker.addEventListener("message", receivedWorkerMessage);

function receivedWorkerMessage(event) {
    var message = event.data; // 消息内容

    ...
}

After the worker finishes its task, the main thread closes the worker with worker.terminate().

2. Worker sub-threads

The main thread sends a message to the worker thread by triggering the message event via worker.postMessage, and the worker can receive messages from the main thread by listening to the message event.

The global object in the child thread is the self keyword, not window. worker’s message event is the same as other dom events, so there are several ways to listen for events.

(1) Use self.addEventListener().

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
self.addEventListener('message', function (event) {
    const message = event.data;

    ...根据消息内容进行一系列任务通常是耗时的任务

};, false);

// self为全局对象所以可以省略
addEventListener('message', function (event) {
    const message = event.data;

    ...根据消息内容进行一系列任务通常是耗时的任务

};, false);

(2) Use self.onmessage().

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
self.onmessage = function (event) {
    const message = event.data;

    ...根据消息内容进行一系列任务通常是耗时的任务
};

// self为全局对象所以可以省略
self.onmessage = function (event) {
    const message = event.data;

    ...根据消息内容进行一系列任务通常是耗时的任务
};

3. Worker can load scripts

Worker can load scripts via importScripts().

1
2
3
4
5
// 加载单个脚本
importScripts('script1.js');

// 加载多个脚本
importScripts('script1.js', 'script2.js');

4. Listening for errors

The main thread can listen for errors. If an error occurs, Worker will trigger the error event.

(1) worker.onerror() .

1
2
3
4
5
worker.onerror = workerError;

function workerError(error) {
    ...
}

(2) worker.addEventListener

1
2
3
4
5
worker.addEventListener("error", workerError);

function workerError(error) {
    ...
}

5. worker is done, remember to close

1
2
3
4
5
// 主线程
worker.terminate();

// Worker 线程
self.close(); // self可以省略

III. A Case

The following is a demo to find all the prime numbers in a specified range of numbers. It is rather time consuming, so the process of finding and calculating is put into the Worker to handle. You can click the following link to see the running effect.

https://codepen.io/AhCola/pen/abLzjEN