Multithreading in JavaScript, Worker and SharedWorker

As the development of the web technical, Figuring large data in web front-end becomes more and more common. But unfortunately, when we try to deal with the work which needs the vast resource, It always blocks the UI thread so that you could do nothing but wait for it works out. In this situation, W3C brings the Web Worker. It allows the browser to start a new background thread to deal with the complicated compute.

1. The block of UI

Let us see what can cause UI block.

The Demo Link (Code Link: withoutWorkers.html)

In this demo, we can see an updating timer which updates the timestamp all the times. Also, we have a button on that page.

When we trigger the button, the JavaScript will try to work out the 43rd Fibonacci sequence(https://en.wikipedia.org/wiki/Fibonacci_number). That needs lots of compute resource, So, After we click the button, the UI in this page is blocked, after a while it back to normal and alert the result: the resoult of the demo

Fibonacci sequence is only one of the thousands situation. In fact, We have so many similar cases, How could we figure it out?

2. Meet the Web Worker

Demo first:

Demo Url: use Worker get the Fibonacci sequence (Code URL: 1. withWorkers.html 2. workers.js )

It is an entirely same page, at first sight. But when we click the button, amazing things happened, for some while the result is worked out, At the same time, we find that UI is NOT BLOCK during the compute time.That is fantastic Web Worker!

2.1 Init Worker

Above we used the Web Worker, now let's learn how to use Worker.

We can create worker through new Worker(), Worker receives one param, that is the path the file which contains the worker's script, the path must follow the Same-origin policy (https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy). The code below uses the script workers.js at same folder, while we created the Worker, the browser will start a new background thread to run the script.

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

2.2 Community with Worker

After creating new Worker, what we care about is how to communicate with the worker. We use postMessage to solve this.

2.2.1 postMessage(Object aMessage [,sequence transferList])

In the master thread, we can use worker.postMessage to send the message to the background worker, postMessage receives two params, the first one is what we want to pass to the worker, the second param is used to define the origin which can receive the message.

button.addEventListener('click', function () {
    worker.postMessage('start');
});

The code above, we listen to the click event for the button. After clicking the button, we send a string which is 'start' to the worker. We can use 'onmessage' event in the worker to get this message. onmessage receives MessageEvent object (https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent), we only need the data of the MessageEvent object that is what we sent, other property like origin, source, etc. you can check it by yourself.

// workers.js
onmessage = function (messageEvent) {
    switch (messageEvent.data) {
        case 'start':
            let result = fabonacci(42);
            postMessage(result);
    }
}

In the code, we listed the data from the master, if the message is 'start' we run 'fabonacci(42)' and then pass the result to master thought postMessage. In the master, we should use worker.postMessage to post the message to a particular worker, but since the worker only has one mater, so in the worker, we can just use postMessage to send the message to the master.

worker.onmessage = function (messageEvent) {
    alert('The result is ' + val.data + ',used ' + (new Date() - time) + 'ms');
}

2.2.2 terminate()

If we want terminate out worker, we can merely use terminate function.

var myWorker = new Worker('worker.js');
myWorker.terminate();

3. SharedWorker

The worker can help us build the elegant website, to improve our user experience. But what is SharedWorker? Guse from the name, it can create a shared worker, that means the different page can share the same worker.

Demo first: SharedWorker Demo (代码 1. sharedWorker.html 2. sharedworkers.js)

In the demo, once you click the button, the counter number will plus one. Let's click several times, then open a new window click the button. You will find that the number does not begin form 0, it starts with the amount which the previous page is. Let's open one more page, the same thing happens. Now let's close all the pages, and then open one page again, you will found that the number is back to 0.

In one word, sharedWorker will only start one background thread during life circle even you opened may pages. Once all the pages are closed the thread will be terminated.

3.1 Create sharedWorker

Similar to the worker, create sharedWorker is easy too,

new SharedWorker(aURL, name);

aURL is the path of the sharedWorker's script. The second param is the name of the thread. The same name will use the same thread(also follow the same-origin policy)

In our demo, we build sharedWorker like this:

let worker = new SharedWorker('sharedworkers.js');

3.2 Communication with sharedWorker

shareWorker is an instance of SharedWorkerGlobalScope (https://developer.mozilla.org/zh-CN/docs/Web/API/SharedWorkerGlobalScope), it's a little different with the worker.

In sharedWorker, if you want to get the Message object, you should use sharedWorker.prot, and the usage of postMessage and onmessage as same as the worker.

For example, if you want to pass the 'start' through postMessage:

worker.port.postMessage('start');

And we use onmessage to receive the data from the background thread.

worker.port.onmessage = function (val) {
    timeDom.innerHTML = val.data
}

There is a difference with addEventListener, if you use addEventListener to bind message Envet (Not .onmessage), you need to run start() first!

worker.port.start();
worker.port.addEventListener('message', function(e) {
    // ... 
});

In background thread there also have something different, we should use onconnent to get the connection with sharedWorker, and we can get the MassageEvent from the first param's ports. With the MassageEvent we can use .onmessage and .postmessage directly.

// sharedworkers.js
onconnect = function (e) {
    // get port from e.prots
    var port = e.ports[0];

    // port.onmessage listen the message from master
    port.onmessage = function () {
        // port.postMessage send message to master
        port.postMessage(a++)
    }
}

3.3 Debug with sharedWorker

When we develop the sharedWorker programs, we found that sharedWorker is run in the background, we can't debug it directly. While the first time I develop with sharedWorker, I found those problems:

  1. the console.log of sharedWorker is not shown in the developer tools
  2. when we update the script of the sharedWorker, it's will not restart with the new code.

In fact, we can debug sharedWorker in chrome through the address chrome://inspect/

Open the page, choose Shared Workers, we can find all the sharedWorker, there also have two buttons, inspect and terminate.

inspect

When you try to click inspect, you'll find that all you need is there.

inspect2

Overall

Worker and SharedWorker give us more space to developer web pages, we can use this two APIs to develop more friendly and more efficient applications, also SharedWorker can pass data through different pages, it gives a new clue to developing new applications.

136540
  • logo
    • HI, THERE!I AM MOFEI

      (C) 2010-2024 Code & Design by Mofei

      Powered by Dufing (2010-2020) & Express

      IPC证:沪ICP备2022019571号-1