preface
The previous assignment required to write a logistic regression regularization. As the gradient descent algorithm would take a long time to calculate, the total calculation time would be about one and a half minutes, during which the page would be in an unresponsive state. Therefore, I thought of using Web Worker to ensure the smoothness of the main page.
I. Introduction to Web Worker basics
Web workers are used to create multiple threads to help share tasks that are more time-consuming than execution without affecting the smoothness of the main thread. Compared to other asynchronous solutions, Web workers are truly thread free. The global object that the Web Worker belongs to is different from the main thread and cannot operate DOM and window directly. However, you can use many of the features of Window, such as WebSocket, IndexDB, etc.
There are three types of Web workers:
-
Dedicated Worker: Instantiated by the main thread and can only communicate with it, not access other environments.
-
Shared Worker: All Shared Worker instances share a global environment where multiple pages can communicate with each other and handle multiple connections using the Shared Worker.
-
Service worker: Has the characteristics of Shared workers, is event-driven, can be shut down and restarted at any time, does not need any page to work, only supports HTTPS and localhost. It is mainly used in offline scenarios
The most commonly used, Dedicated Worker is the most common, and Dedicated Worker has good compatibility. Therefore, this paper mainly introduces Dedicated Worker, and uses Worker to refer to Dedicated Worker.
Two, basic usage
1. Basic method of use
Main thread method:
Var worker = new worker ('work.js') var worker = new worker ('work.js') // To create a new worker, the parameter is the url of the script file, which is restricted by the same origin policy worker.postmessage (message) // to send messages to child threads, Arguments can be of any type and pass a copy of the data, not a reference. Onmessage = function (event) {console.log(event. Data); . } // Receive the message returned by the child thread and store the data in the event.data worker.terminate() // terminate the thread method. The worker will not be destroyed automatically after it is established, so it needs to be manually controlled and destroyed. It should be noted that this is a synchronous method and must be used in the callback according to the situation, otherwise the child thread will be terminated immediately.Copy the code
Child thread methods:
this.addEventListener('message', function (e) { this.postMessage(e.data); }, false) // this.addeventListener is used to listen for messages from the main thread, this.postMessage is used to send messages to the main thread. This represents the child thread itself.Copy the code
The above methods can be used in most scenarios, and more complete instructions can be found in the official documentation and links below.
2. Create a worker to communicate with the main thread
<script id="worker" > this. AddEventListener ('message',function(e){console.log(e.ata)) // returns a message to the main thread This.postmessage (' IT's all about you ')},false) </script> <script> // The inline script construction is used here, which avoids the same-origin policy restriction. The Blob method converts the object to binary, and url.createObjecturl generates the web URL for the Blob object. var blob = new Blob([document.querySelector('#worker').textContent]); var url = window.URL.createObjectURL(blob); var worker = new Worker(url); Worker. postMessage('oh honey'); Worker. onMessage =function(e){console.log(e.ata){worker.terminate()} </script>Copy the code
The running results are as follows:
Three, the order of execution
It is necessary to know the order of execution of the child threads. The child threads have their own task queue, which complies with the characteristics of the task queue and does not interfere with the main thread. A child thread can fire multiple times, returning data in the order in which the child thread’s task queue is executed. As shown in the figure below, three tasks are triggered, all of which are asynchronous.
This. AddEventListener ('message',function(e){setTimeout(() => { This.postmessage (' child time ${e.data} ')}, e.data) },false) </script> <script> var blob = new Blob([document.querySelector('#worker').textContent]); var url = window.URL.createObjectURL(blob); var worker = new Worker(url); // trigger three tasks const time=[10000,5000,2000]; for(var i=0; i<3; i++) { worker.postMessage(time[i]); Worker.onmessage =function(e){console.log(e)} </script> worker.onmessage=function(e){console.log(e)} </script>Copy the code
The execution result is shown as follows:
Three asynchronous tasks are triggered to form a task queue, and the child threads execute in the order of execution.
Fourth, control the running of threads
In the previous paragraph, it is worth noting that when the child thread is triggered multiple times, the complexity of the task is different and the time of the task is different. It may be that the child thread is executing the task and a new task is added. The result is that these tasks can merge unpredictably, causing confusion in data return.
For example, if you add a synchronous task in the above example, the synchronous task will be executed before the asynchronous task.
Therefore, it is best to wait for the last task to complete and then trigger the next task. Create the worker class:
class myWorker{
constructor(url){
this.queue=[];
this.worker=new Worker(url);
this.worker.onmessage=(e)=>this.queue.shift().resolve(e.data);
this.worker.onerror=(e)=>this.queue.shift().reject(e.data);
}
post(params){
return new Promise((resolve,reject)=>{
this.queue.push({resolve,reject});
this.worker.postMessage(params)
})
}
}
Copy the code
Use async functions to control the order of execution
Var worker = new myWorker() const time=[1000050002000] async function dispatch(time){while(time.length) {var res=await worker.post(time.shift()) console.log(res) } } dispatch(time)Copy the code
The results are as follows:
Control the child thread, then the child thread destruction is easy to control.
Five, the application of
1. Time-consuming task processing
This is the most common usage and is useful when dealing with time-consuming tasks. The child thread code in the job looks something like this:
Define handler functions and some initialization data inside, and wait for incoming data from the main thread to start.
The main thread is only responsible for passing in data and waiting for the child thread to finish rendering the data, much like an asynchronous callback function. The main thread looks like this:
The final result is shown below:
You can see that it runs very smoothly, the mouse can be arbitrarily swiped, the page is not stuck. Other time-consuming tasks include high-frequency user interaction, such as assisting users with input error correction and correction based on their input habits, historical records, and cache information.
2. The Worker thread completes the polling
The core code of the child thread is to periodically request data to the server and return after comparison
setInterval(function () { fetch('url').then(function (res) { var data = res.json(); if (! compare(data, cache)) { cache = data; self.postMessage(data); }})}, 1000)});Copy the code
3. Progressive network application.
Using features such as IndexDB to store data requests locally for fast loading even when the network is unstable must be performed by Web Workers in order not to block UI thread rendering. You need to use the Service Worker. For details, see the link below (developer.mozilla.org/zh-CN/docs/…).
Reference links:
Blog.sessionstack.com/how-javascr…
Developer.mozilla.org/en-US/docs/…
www.ibm.com/developerwo…
Developer.mozilla.org/en-US/docs/…).