JavaScript multithreading programming

  javascript, Multithreading

Stay away from browser Caton, improve user experience, improve code running efficiency, and use multi-threaded programming methods.

The browser-side JavaScript is executed in a single thread mode, that is, JavaScript and UI rendering occupy the same main thread, which means that if JavaScript carries out high-load data processing, UI rendering is likely to be blocked, and the browser will be blocked, thus reducing the user experience.

To this end, JavaScript provides asynchronous operations, such as timer (setTimeout, setInterval) events, Ajax requests, I/O callbacks, etc. We can use asynchronous processing for high-load tasks. They will be put into the event loop of the browser. The event queue will not be executed one by one according to the first-in-first-out principle until the JavaScript runtime execution thread is idle.

nodejs引以为荣的异步处理

Asynchronous programming methods such as timers and callback functions are sufficient in normal work, but if complex operations are done, the shortcomings of this method will gradually manifest themselves, such as incorrect values obtained by settimeout, or false dead state easily triggered when pages have complex operations, asynchronous codes will affect the code execution of the main thread, asynchronous is still a single thread after all, and cannot fundamentally solve the problem.

Multithreading (Web Worker) came into being as the times require. It is part of the HTML5 standard, which defines a set of API that allows a JavaScript program to run in another thread besides the main thread. Assign some tasks to the latter to run. While the main thread is running, the Worker (sub) thread is running in the background, and the two do not interfere with each other. Wait until the Worker thread completes the calculation task before returning the result to the main thread. This has the advantage that some computationally intensive or high-latency tasks are shouldered by the Worker thread, and the main thread (usually responsible for UI interaction) will be smooth and will not be blocked or delayed.

What is web worker?

图片描述

Worker is a method of window object, which is used to create multithreading. You can check whether your browser supports worker in the following ways

if (window.Worker) {…… your code ……}

A Worker is an object created using a constructor (worker ()), which needs to pass in a JavaScript file containing the code to be run in the worker thread. Like this:

let myWorker = new Worker('worker.js');

The data of the main thread and the sub-thread are not shared, and the worker communicates with the onmessage event through the postMessage () method. The main thread and the sub-thread are bidirectional and can both send and listen for events. This is required to send a message to a worker (main.js):

myWorker.postMessage('hello, world');  //Send
On message = function (event) {//receive
console.log('Received message ' + event.data);
doSomething();
}

The data passed by postMessage are all copy passed (except ArrayBuffer type), so the sub-thread is similar to pass (worker.js)

addEventListener('message', function (e) {
 postMessage('You said: ' + e.data);
 }, false);

When the sub-thread runs and is used up, in order to save system resources, the sub-thread can be closed manually. If the worker does not listen for messages, it will shut down automatically when all tasks (including counters) are completed.

//Close in main thread
worker.terminate();
//thread in child thread
close();
Worker also provides an error handling mechanism that triggers an error event when an error occurs.
//Monitor error Event
worker.addEventListener('error', function (e) {
console.log('ERROR', e);
});

Web worker itself is very simple, but it has many limitations.

Problems in use

1. Homologous restriction

The script file (Worker.js) assigned to the worker thread to run must be homologous to the script file (main.js) of the main thread. Homologous restrictions here include protocol, domain name and port, and local address (file://) is not supported. This will bring about a problem. We often use CDN to store js files. The domain name of worker.js of the main thread refers to the domain where html files are located. The url loaded through new Worker(url) belongs to the domain of CDN, which will bring about cross-domain problems. In actual development, we will not put all the codes in one file and let the sub-threads load. We will definitely choose modular development. Merge the code into a file through tools or libraries, and then generate a file url from the code of the child thread.
Resolution:
(1) Convert dynamically generated scripts into Blob objects.
(2) Then create a URL for this Blob object.
(3) Finally, the created URL is used as an address to the constructor of Worker.

let script = 'console.log("hello world!"  );  '
 let workerBlob = new Blob([script], { type: "text/javascript" });
 let url = URL.createObjectURL(workerBlob);
 let worker = new Worker(url);

2. Access Restrictions

The global object where the Worker sub-thread is located is not in the same context as the main thread, so it cannot read the DOM object of the web page where the main thread is located, nor can it use the document, window and parent objects. the direction of the global object has changed, the window needs to be rewritten into self, the alert () method and confirm () method cannot be executed, and only some data in the navigator object can be read. In addition, chrome’s console.log () can be used, and it also supports debugger breakpoint to increase debugging convenience.

3. Use asynchrony

You can use XMLHttpRequest objects to make AJAX requests in Worker subthreads, you can use the setTimeout() setInterval () method, or you can use websocket for continuous linking. Additional script files can also be loaded through importScripts(url), but still cannot be cross-domain.

Application scenario:

1. Use dedicated threads for mathematical operations

The original intention of Web Worke design is to do time-consuming tasks of computation and processing of big data. However, this kind of computation in the worker will not interrupt the operation of front-end users and avoid unnecessary user experience caused by code blocking. For example, processing large quantities of data returned by ajax, reading user uploaded files, calculating MD5, canvas bitmap filtering, analyzing video and audio files, etc. In addition to the lack of DOM and BOM operation capabilities, worker still has very strong js logical operation processing capabilities, equivalent to a level of nodejs operating environment.

2. High-frequency user interaction

High-frequency user interaction is suitable for assisting users to complete input error correction and correction functions and other similar scenes according to user input habits, history records, cache and other information. Response processing frequently input by users can also be considered to be executed in web worker. For example, we can make an application like Word: when the user types, the background will immediately search the dictionary to help the user automatically correct errors, etc.

3. Pre-fetching of Data

For some front-end and back-end interactive products with a large amount of data, a new thread can be opened specifically for prefetching and buffering data. worker can be used to write and change rows in the local web database. It can run continuously for a long time without interruption by activities on the main thread (such as users clicking buttons and submitting forms), and is also conducive to responding to the communication of the main thread at any time. It can also cooperate with XMLHttpRequest and websocket to carry out continuous communication to realize the guarding process.

Compatibility

图片描述

Generally speaking, the compatibility is still good. The mobile terminal can use it at ease, and the desktop terminal can also use it if the requirements are not high.

superWorker

In order to use the web worker more conveniently and quickly, we have packaged a tool, which can write scripts running in the web worker in a modular way, avoid homologous policies, reduce the server sending an additional url request, and do not need to know the web worker. just like using setTimeout, we can use superWorker quickly to improve your coding efficiency and running efficiency. it has the following advantages:
1. Native JS implementation without any dependency library.
2. It is simple and fast. It abandons tedious creation of files and binding events, and realizes non-invasive and non-aware code for running new threads.
3. Return Promise type data, support chain call, clear.
4. Support various ways to create a new worker, including anonymous function, function list, text file, html fragment, url, class, which is convenient and fast.
5. gzipped is only 1.2kb after compression.

Using tutorials:

import superWorker from 'superWorker'
 let worker = superWorker(function (a, b) {
 //code to be run in child thread
 return a + b;
 });
 worker.start(1, 2).then((r)=>console.log(r));  // 3

usage

superWorker(code, [type])

Parameter

Code: running code, type (optional): code type, currently supports 0, 1, 2, 3 and 4.

Implementation principle:

First, transfer the source code to file:

let workerBlob = new Blob(code, { type: "text/javascript" });
let url = URL.createObjectURL(workerBlob);

For type splitting, the code parameter supports functions such as incoming anonymous functions, function lists, text files, url, HTML embedded tags, classes, etc. first, the incoming codes are classified and matched, string-typed, and then spliced

code = `(${Function.prototype.toString.call(code)})(${exportsObjName})`;

For the passed-in method, mark the exports object in the main thread and assign values to the exportsObjName object in the worker sub-thread. For ES6 modular code, filter and translate.

//process \ nexport defaultfunctionxxx () {} = > exports.default = true;  exportsObjName.default = function xx(){}
code = code.replace(/^(\s*)export\s+default\s+/m, (s, before) => {
exports.default = true;
return `${before}${exportsObjName}.default=`;
});

The methods in the main thread exports and the child thread exportsObjName are formed to correspond one to one.

The communication between the worker main thread and the main thread still needs to be carried out through the postMessage method and the onmessage callback event. This is a unified two-way binding. setup is executed for the main thread and the sub-thread.

function setup(ctx, pmMethods, callbacks) {
ctx.addEventListener('message', ({ data }) => {
// ……
})
}

The main thread encapsulates some quick methods for worker, such as closing the thread:

worker.terminate = () => {
URL.revokeObjectURL(url);
term.call(this);
};

And expose the methods and attributes owned by the child thread, which is convenient for the main thread to call through passing parameters.

worker.expose = methodName => {
worker[i] = function () {
return worker['call'](methodName, [].slice.call(arguments));
};
};

The general picture is as follows:
图片描述

Welcome friends to use and criticize. If you have any questions, please give us more feedback and communicate more.

Summary

As for the new technology of web worker, it is very practical both on PC and on mobile web. Tencent News front-end group has made extensive attempts. The implementation of Web Worker has brought back-end computing capability to front-end programs and realized the separation of main UI thread and complex computing thread, thus greatly reducing the situation of interface rendering card and frame dropping caused by UI blocking due to large amount of computation, and making greater use of the performance of terminal hardware. SuperWorker can solve complicated problems such as event binding and homology strategy. At present, its biggest problem is that it is incompatible with IE9. In places where compatibility requirements are not so strict, use it as much as possible.

Finally,TNFE teamFor front-end developers, the latest high-quality content in small programs and web front-end technologies has been compiled. It is updated weekly. Welcome to star, github address:https://github.com/Tnfe/TNFE-Weekly