📜 ⬆️ ⬇️

Workers and shared workers

All popular languages ​​have threads. In browser javascript, workers are used for parallel processing.
Under the cut there is a story about how to use them, what limitations exist in the workers and about the peculiarities of interaction with them in different browsers.


What is a worker


A separate context for performing background tasks that does not block the UI. Usually a worker is created as a separate script, the worker’s resources live in the process of the page that created it. The shared worker is the same, but can be used from several pages.
In worker there are:


Creating a worker


Worker is created from a separate script:
var worker = new Worker(scriptUrl); var sharedWorker = new SharedWorker(scriptUrl); 

The shared worker is identified by a URL. To create a second worker from one file, you can add any parameter to the URL (worker.js? Num = 2).
')
Worker can be created without a separate file. For example, create it from the function text:
 var code = workerFn.toString(); code = code.substring(code.indexOf("{")+1, code.lastIndexOf("}")); var blob = new Blob([code], {type: 'application/javascript'}); worker = new Worker(URL.createObjectURL(blob)); 


You can create a worker from a worker only in Firefox. In Chrome, you can create a shared worker from a page and transfer its port to another worker (see below).

Constraints of workers


Dom


In a worker, you cannot use DOM, instead of a window, the global object is called self. Cannot access localStorage and draw on canvas. The same limitations are usually found in all desktop APIs: access to windows only from the UI thread.

Access to objects


From worker it is impossible to return object. There are no lock-s and other thread-safety features in javascript, so objects cannot be transferred from a worker by reference, everything sent to or will be copied from the worker.

CORS


So far, workers do not support CORS, you can create a worker only by downloading it from your domain.

Stack size


For workers, a smaller stack size is allocated, sometimes it matters:
Chrome / osxFirefox / osxSafari / osxChrome / winFirefox / winIE11 / win
web20,80048,00063,00041 90051 00063,000
worker5 30043 3006,10021 30037,00030 100

console


Until recently, it was not, but usually now there is. In some browsers, there is no console in the worker, so it’s better to check its availability before accessing it.

Interaction with worker


After creating a worker, you can send him a message:
 worker.postMessage({hello: 'world'}); worker.onmessage = function(e) { e.data ... }; sharedWorker.port.postMessage({hello: 'world'}); sharedWorker.port.onmessage = function(e) { e.data... }; 


Subscribe to a message in worker as follows:
 // worker self.onmessage = function(e) { e.data... }; // shared worker self.onconnect = function(e) { var port = e.ports[0]; port.onmessage = function(e) { e.data... }; }; 


Similarly, back from a worker, you can call either self.postMessage or port.postMessage for shared workers.

The postMessage method uses the structured clone algorithm for cloning objects. This is not the same as serialization in JSON. The algorithm is able:

But can not:


Transferables


You can send something by reference. For this, there is a second parameter in postMessage, transferList :
 var ab = new ArrayBuffer(size); worker.postMessage({ data: ab }, [ab]); 

In transferList you can transfer a list of objects that will be moved. Only ArrayBuffer and MessagePort are supported. In the calling context, the object will be cleared (neutered): the ArrayBuffer will have a zero length, and trying to send it again will result in an error:
 Uncaught DOMException: Failed to execute 'postMessage' on 'Worker': An ArrayBuffer is neutered and could not be cloned. 


The interaction of two workers


In Firefox, you can create a worker from a worker (standard defines subworkers ).
Now in chrome, you cannot create a worker from a worker, and sometimes workers need to interact with each other. The easiest way is to make the transfer of messages from one to another through the page code. But this is inconvenient because: 1. it is necessary to write additional code, 2. increases the number of interactions and data copying by 2 times, 3. requires execution of the code in a UI context.
The Worker can be taught to communicate with a shared worker by passing him the port of a shared worker, while losing the transmitted port in a UI context; if it is needed, it will be necessary to reconnect to the shared worker, creating it again. The transfer port looks like this:
 worker.postMessage({ port: sharedWorker.port }, [sharedWorker.port]); //  worker-      -   

True, the V8 engine uses the UI context for synchronization, as can be seen by hanging the page for a while: the workers continue to work, and the postMessage do not go between them, waiting for the UI context to be specific.

PostMessage performance


Performance is different for several cases, different data sizes and use of transferList (trlist):

The table shows the number of data transfer cycles from the worker and back per second.
Chrome / osxFF / osxSafari / osxChrome / winFF / winIE11 / win
dedicated: 10B9 3008,40021,0006,8007 3003,200
dedicated: 10kB4,0007,0005,0003,0005,0001,800
dedicated: 1MB805009060400200
dedicated: 10MBeight407752thirty
dedicated: trlist: 10MB8,4001,1002,5006,2001,9002,200
shared: 10B3,1008 300-2,2005 500-
shared: 10kB1,8006,900-1,4004,500-
shared: 1MB40500-32400-
shared: 10MBfour40-four53-
shared: trlist: 10MB-260--1,800-
shared-ipc: 10B3,000--2 700--
shared-ipc: 10kB1,600--1,700--
shared-ipc: 1MB40--thirty--
shared-ipc: 10MBfour--3--

Conclusions that can be drawn from the data:


Killing a worker


A decicated worker can be killed by calling worker.terminate (). It’s not possible with shared worker, its execution will be terminated:

Let's try to call the process crash from a shared worker. Together with the worker, of course, the tab that created it will also fall. In the tab where it was still used, we will see the following message:


Unfortunately, now there is no regular way to track the closure of the worker or the page that uses it.

Resource accounting in shared worker in Chrome


SharedWorker lives by the process in the page that created it. It takes into account and displays in the task manager CPU and the memory that the worker consumes. If the page is closed, its worker process will return the memory used by the page (not immediately, some time after closing) and will remain alive while other pages use this worker. Interestingly, this process will completely disappear from chrome statistics: neither the memory nor the CPU will be able to be tracked by the user in his internal task manager. This is unpleasant, because the user is most likely not to guess why the browser began to consume so many resources.

Debugging workers


In chrome shared, workers are available on the chrome: // inspect / # workers page:

This is where the console output from the worker is written.
The dedicated worker in chrome and IE is debugged in the page where it is executed:

In other browsers with debugging, workers are bad for now.

Can I Use ...


Support for different workers on Can I Use . Briefly, with reference to today's web: the worker is on modern browsers, sharedworker on advanced desktop browsers, serviceworker is too early.

...


Everything written is relevant for the summer of 2015, do not forget that the web is changing rapidly.

Links


Using Web Workers (MDN)
The Basics of Web Workers
Living Standard: Web workers
Transferable objects
How fast are web workers?

Source: https://habr.com/ru/post/261307/


All Articles