JS Multi-Process

WebWorker

It has brothers of ServicesWorker, AudioWorker, Chrome Workers …

API that allows Web application authors to spawn background workers running scripts in parallel to their main page. This allows for thread-like operation with message-passing as the coordination mechanism. The advantage of this is that laborious processing can be performed in a separate thread, allowing the main (usually the UI) thread to run without being blocked/slowed down.

A worker is an object created using a constructor (e.g. Worker()) that runs a named JavaScript file — this file contains the code that will run in the worker thread; workers run in another global context that is different from the current window. This context is represented by a DedicatedWorkerGlobalScope object in the case of dedicated workers (standard workers that are utilized by a single script; shared workers use SharedWorkerGlobalScope).

you can use a large number of items available under window, including WebSockets, and data storage mechanisms like IndexedDB and the Firefox OS-only Data Store API. See Functions and classes available to workers for more details.

Due to their multi-threaded behavior, web workers only has access to a subset of JavaScript’s features:

  • The navigator object
  • The location object (read-only)
  • XMLHttpRequest
  • setTimeout()/clearTimeout() and setInterval()/clearInterval()
  • The Application Cache
  • Importing external scripts using the importScripts() method
  • Spawning other web workers

Workers do NOT have access to:

  • The DOM (it’s not thread-safe)
  • The window object
  • The document object
  • The parent object

Data Transfer

Messages passed between the main page and workers are copied, not shared. Most browsers implement the structured cloning algorithm, which allows you to pass more complex types in/out of Workers such as File, Blob, ArrayBuffer, and JSON objects. However, when passing these types of data usingpostMessage(), a copy is still made.

Structured cloning is great, but a copy can take hundreds of milliseconds. To combat the perf hit, you can use Transferable Objects.

With Transferable Objects, data is transferred from one context to another. It is zero-copy, which vastly improves the performance of sending data to a Worker. Think of it as pass-by-reference if you’re from the C/C++ world. However, unlike pass-by-reference, the ‘version’ from the calling context is no longer available once transferred to the new context. For example, when transferring an ArrayBuffer from your main app to Worker, the original ArrayBuffer is cleared and no longer usable. Its contents are (quiet literally) transferred to the Worker context.

  1. worker.postMessage({data: int8View, moreData: anotherBuffer},
  2. [int8View.buffer, anotherBuffer]);

Load External Scripts in Worker

importScripts('utils.js', 'store.js');

And you can also use inline worker

var blob = new Blob([
    "onmessage = function(e) { postMessage('msg from worker'); }"
]);
// Obtain a blob URL reference to our worker 'file'.
var blobURL = window.URL.createObjectURL(blob);
var worker = new Worker(blobURL);

Sub-workers

Workers have the ability to spawn child workers. This is great for further breaking up large tasks at runtime. However, subworkers come with a few caveats:

  • Subworkers must be hosted within the same origin as the parent page.
  • URIs within subworkers are resolved relative to their parent worker’s location (as opposed to the main page).

Keep in mind most browsers spawn separate processes for each worker. Before you go spawning a worker farm, be cautious about hogging too many of the user’s system resources. One reason for this is that messages passed between main pages and workers are copied, not shared. See Communicating with a Worker via Message Passing.

For an sample of how to spawn a subworker, see the example in the specification.

Conditions to use Workers

  • Chunk of huge amount of mathematic calculations.
  • Separated network requests or blocked I/O.

  • Prefetching and/or caching data for later use.
  • Code syntax highlighting or other real-time text formatting.
  • Spell checker.
  • Analyzing video or audio data.
  • Background I/O or polling of web services.
  • Processing large arrays or humungous JSON responses.
  • Image filtering in
  • Updating many rows of a local web database.

API

main.js

let errorAlert = false;

(function(window, document) {
  if (!window.Worker) return;

  const worker = new Worker('worker.js');

  // start the worker
  worker.postMessage({ type: 'Greet', msg: 'hello, world!' });

  // report the error one
  worker.postMessage({ type: 'Error', msg: 'cause trouble now!' });

  // this worker won't work because the worker is closed
  worker.postMessage({ type: 'Close', msg: 'close the worker' });

  worker.addEventListener('message', (e) => {
    console.log('Worker said: ', e.data);
  }, false);
  worker.addEventListener('error', (e) => {
    console.log('Worker error:', e);
  })

  setTimeout(() => {
    // terminate a worker
    if (errorAlert) worker.terminate();
  }, 1000);

})(window, document);

worker.js

// the other local scripts
importScripts('utils.js', 'store.js');

console.log('we import function sort and cache', sort, setCache);

self.addEventListener('message', function (e) {
  console.log('worker receive message type:' + e.data.type);
  switch (e.data.type) {
    case 'Greet':
      self.postMessage('I receive message: ' + e.data.msg); 
      break;
    case 'Close':
      self.postMessage('Close the worker wit message: ' + e.data.msg);
      close();
    case 'Error':
      self.postMessage('2'.toPublic());
    default:
      break;
  }
}, false);

Node.js MultiProcess

see Node.md doc.


Ref

Worker