New knowledge storeFront end from entry to groundFor attention, star and suggestions, update from time to time ~

Related series: A Journey of Front-end Foundation from Scratch

Javascript runs in a single-threaded environment, which means you cannot run multiple scripts at the same time. Suppose a user clicks a button that triggers a piece of Javascript code for calculation, and the page cannot respond to the user’s actions until that code is executed.

Introduction to the

Web workers provide an easy way for Web content to run scripts in background threads. Threads can perform tasks without interfering with the user interface.

Web workers are concepts introduced in HTML5 that allow you to load and run one or more separate JavaScript threads within the main thread of a page.

Web workers are divided into two types: Dedicated Web workers and Shared Web workers. Dedicated threads can only be used by the script that created them (one dedicated thread for each main thread), while shared threads can be used by different scripts (one shared thread for each main thread).

The multithreaded programming capability provided by Web workers is different from traditional multithreaded programming (Java, C++, etc.). There is no scope or resource shared between the main program thread and Worker thread or between Worker threads. The only communication between them is an event-based listening message.

The JavaScript language itself still runs on a single thread, and The Web Worker is just a capability/API provided by the browser (the hosting environment).

Application scenarios

The implementation of Web Worker brings the ability of background calculation to the front-end program. We can strip some time-consuming data processing operations from the main thread, thus greatly reducing the situation of interface rendering jam and frame drop caused by UI blockage caused by a large amount of calculation, and making the main thread more focused on page rendering and interaction. To take greater advantage of the performance of terminal hardware;

  • Mathematical operations
  • Big data processing
  • Lazy loading
  • The text analysis
  • Streaming media data processing
  • Canvas drawing
  • The image processing
  • .

Pay attention to

  1. In worker, DOM nodes cannot be directly operated or usedwindowObject default methods and properties. However, Data storage mechanisms such as WebSockets, IndexedDB, and FireFox OS specific Data Store APIS are available
  2. The postMessage() method is used to send messages between workers and the main thread, and the onMessage event handler is used to respond to the Message(which is contained in the data property of the Message event). In this process, data is not shared but copied.
  3. The operation of Web workers does not affect the main thread, but the interaction with the main thread is still restricted by the single thread bottleneck of the main thread. In other words, if the Worker thread interacts frequently with the main thread, the main thread may still block the page because it needs to handle the interaction
  4. A shared thread can be invoked by multiple Browsing contexts, but all of them must be of the same origin (the same protocol, host, and port number)

As long as running in the same parent page, workers can generate new workers in turn.

Understand the basic knowledge, the following dry use of explanation.

Dedicated Web worker (Dedicated Web worker)

Create a worker

Creating a new worker is simple. Call the Worker() constructor to specify a script URI to execute the Worker thread (main.js) :

const myWorker = new Worker('worker.js');
Copy the code

The worker detection

For better error handling control and downward compatibility, it is a good idea to wrap the worker runtime code in the following code (main.js) :

if (window.Worker) {

  ...

}
Copy the code

Receiving and sending of messages

Both the Worker thread and the main thread send messages through the postMessage() method and receive messages through the onMessage event.

Onmessage and postMessage() must hang on worker objects when used in the main thread, but not when used in workers. Inside workers, self and this both represent global objects for child threads.

The following four notations are equivalent for listening for message events.

/ / write 1
self.addEventListener('message'.function (e) {
    // ...
})

/ / write 2
this.addEventListener('message'.function (e) {
    // ...
})

/ / writing 3
addEventListener('message'.function (e) {
    // ...
})

/ / write 4
onmessage = function (e) {
    // ...
}
Copy the code

The data transferred between the main page and the worker is copied rather than shared.

// main.js
const myWorker = new Worker('worker.js')

myWorker.onmessage = function(e) {
    console.log(e.data) / / 24
}

myWorker.postMessage([10.24])


// Worker.js
onmessage = function (e) {
    const data = e.data
    postMessage(data[0] + data[1])}Copy the code

Objects passed to the worker need to be serialized, and then deserialized at the other end. Pages and worker ** do not share the same instance, and the end result is that a copy of the data is generated at the end of each communication. ** Most browsers use structured copy to implement this feature.

The worker thread changes data without affecting the original object in the main thread

Data is transferred by transferring ownership (transferable objects)

Another more high-performance approach is to pass objects of a particular type (transferable objects) to/from a worker. Transferable objects are moved from one context to another without any copy operation. This means significant performance gains when delivering big data.

Unlike passing by reference, once an object is transferred, the version in its original context ceases to exist. Ownership of the object is transferred to the new context. For example, when you transfer an ArrayBuffer object from the main application to the Worker, the original ArrayBuffer is erased and cannot be used. The content it contains is passed (intact) to the Worker context.

var uInt8Array = new Uint8Array(1024*1024*32); // 32MB
for (var i = 0; i < uInt8Array .length; ++i) {
  uInt8Array[i] = i;
}
const myWorker = new Worker('worker.js')

myWorker.postMessage(uInt8Array.buffer, [uInt8Array.buffer]);

console.log(uInt8Array.length); // Length after passing :0
Copy the code

Close the Worker

You can call the worker terminate method to terminate a running worker immediately from the main thread:

myWorker.terminate();
Copy the code

The worker thread is immediately killed without any opportunity to complete its operations or clean up.

Worker threads can also call their own close method to close:

close();
Copy the code

Error handling

Errors can be handled by setting onError and onMessageError callback functions in the main thread or Worker thread.

When the worker encounters an in-process error, its onError event handler will be called. It receives an event called Error that extends the ErrorEvent interface.

The event does not bubble up and can be cancelled; To prevent triggering the default action, the worker can call the preventDefault() method of the error event.

// main.js
myWorker.onerror = function () {
    // ...
}
myWorker.onmessageerror = function () {
    // ...
}

// worker.js
onerror = function () {}Copy the code

Error events have the following three fields:

  • messageA well-readable error message.
  • filenameThe file name of the script in which the error occurred.
  • linenoThe line number of the script file in which the error occurred.

Generate subworker

The worker can generate more workers if needed. These are called subworkers, and they must be hosted within the same parent page. Also, the subworker resolves the URI relative to the parent worker’s address rather than the address of its own page. This makes it easier for workers to record dependencies between them.

Introduce scripts and libraries

The Worker thread can access a global function importScripts() to importScripts, which takes zero or more uris as arguments to import resources; The following examples are all legal:

importScripts();                        /* import nothing */
importScripts('foo.js');                /* Only "foo.js" */ is introduced
importScripts('foo.js'.'bar.js');      /* Introduce two scripts */
Copy the code

Scripts are downloaded in a variable order, but are executed in the order of file names passed in importScripts().

Embedded Worker

There is currently no class of tags that can embed Worker code in a web page like a

<script id="worker" type="javascript/worker">
// This code will not be parsed directly by the JS engine because the type is 'javascript/worker'

// Write the Worker thread logic here
</script>
<script>
    var workerScript = document.querySelector('#worker').textContent
    var blob = new Blob(workerScript, {type: "text/javascript"})
    var worker = new Worker(window.URL.createObjectURL(blob))
</script>
Copy the code

Of course, you can also use it in the following ways:

var myTask = ` onmessage = function (e) { var data = e.data; console.log('worker:', data); }; `;

var blob = new Blob([myTask]);
var myWorker = new Worker(window.URL.createObjectURL(blob));
Copy the code

Worker Context (WorkerGlobalScope)

Workers runs in a different global context than the current Window.

  • An error is reported when getting window
  • Attempts to manipulate the DOM will cause an error

In the case of dedicated workers,

  • DedicatedWorkerGlobalScopeThe object represents the context of the worker.
  • The DedicatedWorkerGlobalScope object (that is, the Worker global scope) can access through the self keyword.
  • eachDedicatedWorkerGlobalScopeObjects are differentevent loop. thisevent loopIt has no browsing context associated with it, and its task queue consists of only events, callbacks, and networking activities.

In addition to the standard set of JavaScript functions (such as String, Array, Object, JSON, etc.), DOM has a variety of functions available to workers. DedicatedWorkerGlobalScope like Window, achieve WindowTimers and WindowBase64.

Navigation related

  • Navigator
  • Location

Time correlation

  • clearInterval()
  • clearTimeout()
  • setInterval()
  • setTimeout

Store related

  • Cache
  • IndexedDB

Network related

  • Fetch
  • WebSocket
  • XMLHttpRequest

other

  • console
  • performance

Shared Web workers

A shared worker can be used by multiple scripts.

Shared workers can be invoked by multiple browsing contexts, all of which must belong to the same origin (same protocol, host and port number). Local debugging also requires access by starting the local server, using the file:// protocol directly opened will throw an exception.

Create a worker

Generating a new shared worker is very similar to generating a dedicated worker, except that the constructor name is different

const myWorker = new SharedWorker('worker.js');
Copy the code

Communication with a shared worker must be through a port object — an exact open port for the script to communicate with the worker

Before sending a message, the port connection must be explicitly opened using either the onMessage event handler or the start() method.

That is, the start() method is used with addEventListener. If we select onMessage for event listening, the start() method will be implicitly called.

Receiving and sending of messages

The postMessage() method must be called by the port object

myWorker.port.postMessage([squareNumber.value,squareNumber.value]);
Copy the code

Compared with the dedicated Worker, there is a global connect() function, in which a POST object needs to be obtained for initialization.

onconnect = function(e) {
  var port = e.ports[0];

  port.onmessage = function(e) {
    var workerResult = 'Result: ' + (e.data[0] * e.data[1]); port.postMessage(workerResult); }}Copy the code
  1. When a port connection is created (for example, when the onMessage event handler is set in the parent thread, or when the start() method is explicitly called), the onConnect event handler is used to execute the code.
  2. Use the ports attribute of the event to get the port and store it in a variable.
  3. Then, add a message handler to the port that does the computation and returns the result to the main thread.

About Thread Safety

The Worker interface generates true OS level threads, and for Web workers, the points of communication with other threads are carefully controlled, which means that you can hardly cause concurrency problems. There is no way to access non-thread-safe components or the DOM, and you need to serialize objects to interact with thread-specific data. So you really can’t make a mistake if you don’t try.

Other types of workers

In addition to dedicated and shared Web workers, there are several other types of workers:

  • ServiceWorkers (ServiceWorkers) typically act as proxy servers in front of web applications, browsers, and networks (if available). They aim, among other things, to create an effective offline experience, intercept network requests, and take appropriate action and update the resources that reside on the server depending on whether the network is available. They will also allow access to push notifications and background synchronization apis.
  • Chrome Workers is a Worker that works only with Firefox. If you are developing add-ons and want to use worker in an extension and have access to JS-ctypes in your worker, you can use Chrome Workers. See ChromeWorker for details.
  • Audio Workers (Audio Workers) make it possible to perform scripted Audio processing directly in the context of Web Workers.

If you learn something new, or get a nice picture on the left, please give it a thumbs up

Reference article:

  1. JavaScript performance tool – Web Worker
  2. Talk about HTML5 Web Workers
  3. Use Web Workers