Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Article « The Basics of Web Workers » (HTML5Rocks) #2

Open
3 tasks
olange opened this issue Nov 12, 2018 · 6 comments
Open
3 tasks

Article « The Basics of Web Workers » (HTML5Rocks) #2

olange opened this issue Nov 12, 2018 · 6 comments
Assignees
Labels

Comments

@olange
Copy link
Owner

olange commented Nov 12, 2018

Reading notes of the primer article of Eric Bidelman (26.07.2010):

HTML5Rocks › The Basics of Web Workers
https://www.html5rocks.com/en/tutorials/workers/basics/

plus some other sources:

WebFundamentals › Service Workers: an Introduction
https://developers.google.com/web/fundamentals/primers/service-workers/

MDN › Web Worker API
https://developer.mozilla.org/en-US/docs/Web/API/Worker/

HTML Living Standard › Web Workers.
https://html.spec.whatwg.org/multipage/workers.html

Merge with links from main READ:

Actions

@olange
Copy link
Owner Author

olange commented Nov 12, 2018

« One thing that's remained a hindrance for JavaScript is actually the language itself. JavaScript is a single-threaded environment, meaning multiple scripts cannot run at the same time. As an example, imagine a site that needs to handle UI events, query and process large amounts of API data, and manipulate the DOM. Pretty common, right? Unfortunately all of that can't be simultaneous due to limitations in browsers' JavaScript runtime. Script execution happens within a single thread. » (source: H5R › Basics of Web Workers)

@olange olange changed the title Article « » Article « The Basics of Web Workers » (HTML5Rocks) Nov 12, 2018
@olange
Copy link
Owner Author

olange commented Dec 19, 2018

Getting Started  — Creating a Worker

See also Experiment 01 · Instantiation of a Web Worker (#3).

« A Web Worker is created with the Worker() constructor, that takes the URL of a script; the script must obey the same-origin policy. » (excerpts from MDN › Web Worker API).

« If the specified file exists, the browser will spawn a new worker thread, which is downloaded asynchronously. The worker will not begin until the file has completely downloaded and executed. If the path to your worker returns an 404, the worker will fail silently. »

Classic scripts

See Experiment 01 · Instantiation of a Web Worker · Classic script

Workers usually run classic scripts. Example of a « Background number-crunching worker » (source):

Main page index.html:

<!DOCTYPE html>
<meta charset="utf-8">
…
<script type="module">
  var worker = new Worker( "worker.js");
  …
</script>

Worker worker.js:

var n = 1;
search: while (true) {
  n += 1;
  for (var i = 2; i <= Math.sqrt(n); i += 1)
    if (n % i == 0)
     continue search;
  // found a prime!
  self.postMessage(n);
}

Module scripts

See Experiment 01 · Instantiation of a Web Worker · Module script

« Workers can instead be instantiated using module scripts, which have the usual benefits:

  • the ability to use the JavaScript import statement to import other modules;
  • strict mode by default;
  • and top-level declarations not polluting the worker's global scope.

Note that unlike classic workers, module workers can be instantiated using a cross-origin script, as long as that script is exposed using the CORS protocol.

Additionally, the importScripts() method will automatically fail inside module workers; the JavaScript import statement is generally a better choice. » (source: HTML Living Standard › Web Workers)

Example of a worker doing « off-main-thread image manipulation » (source):

Main page index.html:

<!DOCTYPE html>
<meta charset="utf-8">
…
<script type="module">
  const worker = new Worker( "worker.js", { type: "module" });
  …
</script>

Worker worker.js:

import * as filters from "./filters.js";

self.onmessage = e => {
  const { imageData, filter } = e.data;
  filters[ filter]( imageData);
  self.postMessage( imageData, [ imageData.data.buffer ]);
};

Note: Module scripts to instantiate a worker requires to enable experimental web platform features inin Chrome 71 currently. Running the above examples displays following error message on console:

Uncaught TypeError: Failed to construct 'Worker': Module scripts are not supported on DedicatedWorker yet.
You can try the feature with '--enable-experimental-web-platform-features' flag (see https://crbug.com/680046)

To start Chrome with the experimental-web-platform-features flag (on Mac OS):

$ open -a "Google Chrome" --args --enable-experimental-web-platform-features

Inline / embedded script

See Experiment 01 · Instantiation of a Web Worker · Embedded script

To create your worker script on the fly, or create a self-contained page without having to create separate worker files, with Blob( [ "…worker code…" ]), you can « inline » your worker in the same HTML file as your main logic by creating a URL handle to the worker code as a string:

Main page index.html:

const workerBlob = new Blob( [`self.onmessage = (msg) => { … };`]);
const workerBlobURL = window.URL.createObjectURL( workerBlob);
const worker = new Worker( workerBlobURL);

Getting Started — Creating a Subworker

See also:

Note:

  • does not work in Safari 12 and Mobile Safari 12 (see above experiment 02).

« Workers have the ability to spawn child workers. This is great for further breaking up large tasks at runtime. Subworkers come with a few caveats: they 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. » (source: H5R › Basics of Web Workers)

Getting Started — Creating a Shared Worker

I did not cover the subject much here, as Shared Workers are not well supported yet by mobile browsers. See following examples of creation and use of Shared Workers:

@olange
Copy link
Owner Author

olange commented Dec 19, 2018

Communicating with a Worker

Via Message Passing

« A Worker can't access the DOM directly. Instead, a Service Worker can communicate with the pages it controls by responding to messages sent via the postMessage interface, and those pages can manipulate the DOM if needed. » (source: WebFundamentals › Service Workers: an Introduction).

« A Worker has two methods:

  • Worker.postMessage() Sends a message — which can consist of any JavaScript object — to the worker's inner scope.
  • Worker.terminate() Immediately terminates the worker. This does not offer the worker an opportunity to finish its operations; it is simply stopped at once.
    ServiceWorker instances do not support this method.

And it has three event handlers:

  • AbstractWorker.onerror An EventListener called whenever an ErrorEvent of type error bubbles through to the worker (inherited from AbstractWorker).
  • Worker.onmessage An EventListener called whenever a MessageEvent of type message bubbles through the worker — i.e. when a message is sent to the parent document from the worker via
    DedicatedWorkerGlobalScope.postMessage(). The message is stored in the event's data property.
  • Worker.onmessageerror Is an EventHandler representing the code to be called when the messageerror event is raised. » (source: MDN › Workers API)

The Structured Cloning algorithm

« The structured clone algorithm is an algorithm defined by the HTML5 specification for copying complex JavaScript objects. It is used internally when transferring data to and from Workers via postMessage() or when storing objects with IndexedDB.

The structured clone algorithm builds up a clone, by recursing through the input object, while maintaining a map of previously visited references, in order to avoid infinitely traversing cycles.

Things that don't work with structured clone:

  • Error and Function objects cannot be duplicated by the structured clone algorithm; attempting to do so will throw a DATA_CLONE_ERR exception;
  • Attempting to clone DOM nodes will likewise throw a DATA_CLONE_ERR exception;
  • Certain parameters of objects are not preserved:
    • The lastIndex field of RegExp objects is not preserved;
    • Property descriptors, setters and getters (as well as similar metadata-like features) are not duplicated. For example, if an object is marked read-only using a property descriptor, it will be read-write in the duplicate, since that's the default condition.
    • The prototype chain does not get walked and duplicated. »

(source: MDN › The structured clone algorithm)

Transferable objects

TODO: see H5R › The Basics of Web Workers › Transferable objects

MDN › Web APIs › Transferable Objects A marker interface, that represents an object that can be transfered between different execution contexts, like the main thread and Web workers. The ArrayBuffer, MessagePort and ImageBitmap types implement this marker interface.

@olange
Copy link
Owner Author

olange commented Dec 19, 2018

Worker environment — An isolated execution context

Worker global context

« Workers run in another global context, that is different from the current window and which is represented by either:

Worker scope

In the context of a worker, both self and this reference the global scope for the worker. You can't make use window, which is not available to Web Workers (see above comments).

The standalone self can actually be used in both contexts, window and workers:

« By using self, you can refer to the global scope in a way that will work not only in a window context (self will resolve to window.self), but also in a worker context (self will then resolve to WorkerGlobalScope.self).

The window.self read-only property returns the window itself, as a WindowProxy. It can be used with dot notation on a window object — that is, window.self — or standalone — self. » (source: MDN › Workers API)

Features available to Workers

« Due to their multi-threaded behavior, Web workers only has access to a subset of JavaScript's features:

  • The navigator and 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:

« However you can use a large number of items available under window, including WebSockets, and data storage mechanisms like IndexedDB — or even WebGL with OffscreenCanvas, in the case of Firefox (only one supporting it).

See Functions and classes available to workers for more details. » (source: MDN › Workers API)

Loading External Scripts

To loads script1.js and script2.js into the worker script worker.js, use the importScripts() method, which takes zero or more strings representing the filenames for the resources to impor — note the URLs are relative to the worker script:

importScripts( "script1.js", "script2.js"); // relative to worker script

@olange
Copy link
Owner Author

olange commented Dec 19, 2018

Error handling

  • ErrorEvent is fired, if an error occurs while a worker is executing;
  • note the worker will often fail silently, if an error occurs during instantiation – for instance, because of a security restriction with local access or same-origin policy.

« The ErrorEvent interface contains three useful properties for figuring out what went wrong: filename – the name of the worker script that caused the error; lineno – the line number where the error occurred, and message – a meaningful description of the error. »

Sample error handler, in main page index.html:

const worker = new Worker( "worker.js");
worker.onerror = (err) => {
  console.error( `Received error ${err.message} (line ${err.lineno} in ${err.filename})`);
  err.preventDefault();
}

Note the err.preventDefault(): it prevents the error to propagate to the browser, if the error was properly handled; remove it, if you want to keep the error event bubbling up.

@olange
Copy link
Owner Author

olange commented Dec 22, 2018

Security

Restrictions with Local Access

  • « Due to Google Chrome's security restrictions, workers will not run locally (e.g. from file://) in the latest versions of the browser. Instead, they fail silently! (the --allow-file-access-from-files flag can be set to circumvent this restriction — however it is not recommended to run your primary browser with this flag set). Other browsers do not impose the same restriction. » (source: H5R › Basics of Web Workers)

Same Origin Considerations

  • « Worker scripts must be external files with the same scheme as their calling page. Thus, you cannot load a script from a data: URL or javascript: URL, and an https: page cannot start worker scripts that begin with http: URLs. » (source: H5R › Basics of Web Workers)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant