This is the second day of my participation in Gwen Challenge

What is a Web Worker

Let’s start with the origin of Webworkers: the documentation on the Web is all about webworkers because JavaScript, one of the best programming languages in the world, is a single-threaded language. You can’t code and create a daemon like Java. As a result, projects run into problems that are difficult to solve when optimizing performance. For example, this time I encountered in the work of file second function.

What benefits can Web workers bring to us

JavaScript engines are single-threaded, and time-consuming I/O operations in JavaScript are handled as asynchronous operations, They include keyboard and mouse I/O input and output events, window size resize events, timer (setTimeout, setInterval) events, Ajax request network I/O callbacks, and so on. When these asynchronous tasks occur, they are placed in the browser’s event task queue and are executed on a first-in-first-out basis until the JavaScript runtime thread is free, but they are still single-threaded. Asynchronous programming (promise, async/await), which seems to be sufficient at ordinary times, will gradually show its limitations when it comes to very complex operations, such as image recognition optimization or transformation, H5 game engine implementation, encryption and decryption algorithm operation, etc. A js process running for a long time will cause the browser to freeze the user interface and degrade the user experience. Is there a way to take complex calculations out of the business logic code and run them without blocking the user interface for feedback? The HTML5 standard adopts the Web Worker specification, which defines a set of apis that allow a JS program to run in a thread other than the main thread. Worker threads allow developers to write background programs that can run for a long time without being interrupted by users to execute transactions or logic, and at the same time ensure timely response of the page to users. Some large amount of computational code can be handed over to the Web worker to run without freezing the user interface.

Types of Web workers

I see this in the documentation as well. I don’t use shared types myself. The types I use in my projects are just proprietary types

  1. Dedicated Worker, Dedicated Web Worker
  2. Shared Worker, Shared Web Worker
  3. A “dedicated Worker” can only be accessed by the page that created it, whereas a “shared Worker” can be shared between the same page opened in multiple tabs in the browser.
  4. In the JS code, the Woker class represents the Dedicated Worker. The Shared Worker class represents Shared Web workers.

How to create worker

The main thread uses the new command to call the Worker() constructor to create a new Worker thread.

const worker = new Worker(./worker.js)
Copy the code

The argument to the Worker() constructor is a script file that is the task to be performed by the Worker thread.

Since the Worker cannot read local files, the script must come from the network. If the download does not succeed (such as a 404 error), the Worker silently fails.

V. Communication between main thread and worker thread and how to terminate worker thread

  • Call the postMessage method to send by listening to the onMessage event receive
  • If at some point we don’t want the Worker to continue running, then we need to terminate the thread by calling the Worker on the main threadterminateMethod or in the corresponding threadcloseMethods:
  1. Application file app.js
// Create the worker instance
const worker = new Worker('./worker.js'); // Pass the path to the worker script file
// Listen for messages
worker.onmessage = function(evt){
  // The main thread sends data in evt.data
  // The main thread can terminate the thread using terminate() after processing the message from the worker thread. terminate() };// The main thread sends messages to the worker thread
worker.postMessage({
  value: 'Main thread sends message to worker thread'
});
Copy the code
  1. Application file worker.js
Self.addeventlistener ('message',function(e){}) or self.onMessage = function(e){})
self.onmessage = function(evt){
  // The worker thread receives a message from the main thread (evt.data)
  // In worker threads, the thread can be terminated by close()
  close()
};
// Send messages to the main thread
self.postMessage({
  value: 'Worker thread sends message to main thread'
});
Copy the code
  1. The most important thing to know about working with a Web Worker is that the JS code it executes is in a completely different scope and does not share scope with the code of the current main thread. In a Web Worker, there is also a global object and other objects and methods, but the code does not access the DOM or affect the appearance of the page.
  2. The global object in a Web Worker is the Worker object itself, that is, both this and self refer to Worker objects. To be clear, just like the previous code in worker.js, this can be replaced with self or even omitted.
  3. In order to facilitate data processing, Web Worker itself is also a minimal operating environment, which can access or use the following data:
    • Minimized Navigator objects include onLine, appName, appVersion, userAgent, and Platform properties
    • A read-only Location object
    • SetTimeout (), setInterval(), clearTimeout(), clearInterval() methods
    • XMLHttpRequest constructor

Vi. Data communication (I copied this paragraph directly from Teacher Ruan Yifeng’s article)

As mentioned above, the communication content between the main thread and the Worker can be either text or object. It should be noted that this communication is a copy relationship, that is, value transmission rather than address transmission. Worker’s modification of communication content will not affect the main thread. In fact, the internal operation mechanism of the browser is to serialize the communication content first, and then send the serialized string to the Worker, who then restores it. Binary data can also be exchanged between the main thread and the Worker, such as File, Blob, ArrayBuffer, etc., and can also be sent between threads. Here’s an example.

/ / main thread
var uInt8Array = new Uint8Array(new ArrayBuffer(10));
for (var i = 0; i < uInt8Array.length; ++i) {
  uInt8Array[i] = i * 2; // [0, 2, 4, 6, 8...]
}
worker.postMessage(uInt8Array);

/ / Worker thread
self.onmessage = function (e) {
  var uInt8Array = e.data;
  postMessage('Inside worker.js: uInt8Array.toString() = ' + uInt8Array.toString());
  postMessage('Inside worker.js: uInt8Array.byteLength = ' + uInt8Array.byteLength);
};
Copy the code

However, sending binary data in copy mode can cause performance problems. For example, if the main thread sends a 500MB file to the Worker, by default the browser will generate a copy of the original file. To solve this problem, JavaScript allows the main thread to transfer binary data directly to child threads, but once it is transferred, the main thread can no longer use the binary data, to prevent the troublesome situation of multiple threads modifying the data at the same time. This method of transferring data is called Transferable Objects. This enables the host thread to quickly deliver data to the Worker, which is very convenient for image processing, sound processing, 3D computing, etc., without performance burden.

If you want to transfer control of the data directly, use the following notation.

/ / main thread
// Transferable Objects format
worker.postMessage(arrayBuffer, [arrayBuffer]);

/ / case
var ab = new ArrayBuffer(1);
worker.postMessage(ab, [ab]);
Copy the code

Vii. Worker’s error handling mechanism

  • Specifically, js inside workers will trigger error events whenever they encounter errors during execution.
  • When an error event occurs, the event object contains three attributes: filename, lineno, and message, which represent the filename of the error, the line number, and the complete error message, respectively.
worker.addEventListener('error'.(evt) = > {
  console.log('-MAIN-: '.'---ERROR---', evt);
  console.log('-filename-:' + evt.filename + '-message-:' + evt.message + '-lineno-:' + evt.lineno);
});
Copy the code

Introduce third-party scripts into worker threads

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
  • The browser loads and runs each listed script. The global object in each script can be used by the worker. If the script cannot be loaded, NETWORK_ERROR is thrown and the following code cannot be executed. The previously executed code (including code executed asynchronously with window.setTimeout()) will still run. Function declarations after importScripts() are still retained because they are always run before other code.
  • In the woker

Use Web workers in front-end projects built using WebPack

First, we need to understand a Webpack loader. worker-loader

  1. Install dependenciesworker-loader
$ npm install -D worker-loader
# or
$ yarn add worker-loader --dev
Copy the code
  1. Use the worker-loader directly in the code
// Add worker.js to app.js
  const MyWorker = require("worker-loader? name=worker.[hash:10].js! ./worker.js"); // You can configure a 10-bit hash value to rename the script file
//const MyWorker = require("worker-loader? inline=true&fallback=false! ./worker.js");

const worker = new MyWorker();
worker.postMessage({a: 1});
worker.onmessage = (event) = >{ / * * / operation };
worker.addEventListener("message".(event) = > { / * * / operation });

Copy the code

::: tip Advantage: The script file for writing worker logic can be named arbitrarily, as long as it is uploaded to worker-loader for processing. Disadvantages: Every time the script file of worker logic is introduced, the code as shown above needs to be written once, and “worker-loader!” needs to be written N(N>=1) more times. ::: 3. Import the worker-loader in the webpack configuration file

{
  module: {
    rules: [{/ / match *. Worker. Js
        test: /\.worker\.js$/,
        use: {
          loader: 'worker-loader'.options: {
            name: '[name]:[hash:8].js'.// inline: true,
            // fallback: false
            // publicPath: '/scripts/workers/'}}}]}}Copy the code

The inline attribute can be set to true to inline the worker as a blob. Note that inlining creates additional chunks for browsers, even for browsers that do not support inlining workers; If the browser wants to disable this behavior, simply set the fallback parameter to false.

Error “Window is not defined” in devServer mode

“Uncaught ReferenceError: “Uncaught ReferenceError: “Window is not defined” at that time, WHEN I was ready to do something big by learning the code of our boss, I happily typed my most famous command NPM run start, unexpectedly all red, and saw that window is not defined, The first thing I thought was that there might be something wrong with self in the worker thread. After struggling for a while, I searched Google for off-site help, and sure enough there was an issue about this error: Webpack 4.0.1 | WebWorker window is not defined the final problem solved only need under Webpack configuration file output, add an attribute to: globalObject: ‘this’

output: {
  ...
  globalObject: 'this',},Copy the code

11 Same Origin Policy

Web workers strictly adhere to the same origin policy. If static resources in webpack are not the same origin as application code, it is likely to be blocked by the browser, and this happens often. There are two solutions to this situation for Web workers.

  1. Set the worker-loader parameter to inline to inline the worker to a BLOB data format instead of using the worker by downloading a script file:
// Worker-loader directly uses the method in the file
const workerFile = require("worker-loader? inline=true&fallback=false! ./worker.js");
{loader: 'worker-loader' options: {inline: True}} Then import workerFile from './worker.js' in the file to use: * /

const worker = new Worker(workerFile);
/* Import workerFile from './worker.js' const blob = new blob ([workerFile]); const url = window.URL.createObjectURL(blob); const worker = new Worker(url); * /
worker.addEventListener('message'.(evt) = >{
  console.log('Listen for messages from worker threads',evt.data);
})
worker.postMessage({value:'I'm coming to the autonomous thread.'})
Copy the code

The resources

How to Use Web Workers