HTML5 Tutorial – Web Workers

imageWeb Workers allow running scripts in the background without blocking or freezing the user interface. By using Web Workers to run non-UI scripts in the background, your application can take advantage of multiple cores on your machine to execute scripts concurrently.

Workers allows you to spawn background workers running scripts in parallel to your main page. This allows for thread-like operation with message-passing as the coordination mechanism. Web Workers are also described as “Workers”.

This means that a long running computation no longer needs to block your user interface. HTML5 Web Workers typically run on separate threads so you can take advantage of multicore CPUs.

Using Workers effectively, help you avoid the slow-scripts warnings that display with JavaScript loops continue over several seconds.

Unresponsive script dialog

Although a lot of focus in the specification is around, the architecture and code can be used on the server side too. The node-webworker module aims to implement as much of the HTML5 Web Workers API as is practical and useful in the context of NodeJS.

Web Worker Scenarios

You will want to consider Web Workers:

* Encoding/decoding a large string that you create on the client and wish to keep it there.
* Code syntax highlighting or other real-time text analysis (like spell checking).
* Background I/O (e.g. Polling web services).
* Complex mathematical calculations (prime numbers, encryption, simulated annealing, etc.) – think on a web app that work offline and still need to run…
* Analyzing or processing video or audio data (including face and voice recognition)

  • Image processing by using the data extracted from the <canvas> or the <video> elements. You can divide the image into several zones and push them to the different Workers that will work in parallel. You’ll then benefit from the new generation of multi-cores CPUs. The more you have, the faster you’ll go.
  • big amount of data retrieved that you need to parse after an XMLHTTPRequest call. If the time needed to process this data is important, you’d better do it in background inside a Web Worker to avoid freezing the UI Thread. You’ll then keep a reactive application.
  • background text analysis: as we have potentially more CPU time available when using the Web Workers, we can now think about new scenarios in JavaScript. For instance, we could imagine parsing in real-time what the user is currently typing without impacting the UI experience. Think about an application like Word (of our Office Web Apps suite) leveraging such possibility: background search in dictionaries to help the user while typing, automatic correction, etc.
  • concurrent requests against a local database. IndexDB will allow what the Local Storage can’t offer us: a thread-safe storage environment for our Web Workers.

Worker Limitations

Workers are relatively heavy-weight, and are not intended to be used in large numbers. Generally, workers are expected to be long-lived, have a high start-up performance cost, and a high per-instance memory cost.

Workers do not have access to the direct access to the Web page or the DOM API. Although they do not block the UI, they do use CPU cycles and can make the system be less responsive.

Workers are not supported in every browser. The latest versions of browsers (IE 10, Firefox 16 and later, Chrome 23 and later, Safari 5.1 and later, Opera 12.1 and later, and others) and Windows 8 applications are supported.

See CanIUse site for supported browsers.

Support through a polyfill (emulation) does not really make sense. For backward compatibility, you can just call your code inline with the down-side being that you will block the UI thread. Or you can mimic ‘concurrency’ by using techniques like setTimeout(), setInterval(), XMLHttpRequest, and event handlers. Although these features run asynchronously, non-blocking doesn’t necessarily mean concurrency. Asynchronous events are processed after the current executing script has yielded.

Check for Support for Workers

You can check to see if Workers are supported in your browser. A call to typeof(Worker) will return the global Worker property. The property will be undefined if your browser does not support Workers.

Using Web Workers

Let’s start with a simple example. Let’s count from 0 to 10 million by ten-thousands.

The main page looks like this:

And in counter.js:

In this example, the page updates a counter until it reaches 10,000,000.

In lieu of counting, you can imagine retrieving or updating data on a Web service.

Creating Web Workers

You can spawn background scripts that run in parallel with the main page. A new worker object requires a .js file, which is included via an asynchronous request to the server.

The code sets up event listeners and communicates with the script that spawned it.

Communicating with Web Workers

In the context of a worker, both self and this reference the global scope for the worker.

Since web workers are in external files, they do not have access to the following JavaScript objects:

  • The window object
  • The document object
  • The parent object

All communication to and from the worker thread is managed through messages. Both the host worker and the worker script can send messages by using postMessage and listen for a response by using the onmessage event. The content of the message is sent as the data property of the event.

Or you can use addEventListener.

Then inside the Worker, in the case of the example in aWorkerTask.js, use postMessage to send a message back to the originating thread.

You can also post serialized data or JSON.

Stopping Web Worker

To terminate a web worker, and free browser/computer resources, use the terminate() method:

Handling Errors

You will want to handle any errors that are thrown in your web workers. If an error occurs while a worker is executing, an ErrorEvent occurs.

You get 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.

For example:

Initializing Worker, Two-way Communication

Messages passed between the main page and workers are copied, not shared. For example, you can pass a parameter inside JSON that is accessible in both locations. It appears that the object is being passed directly to the worker even though it’s running in a separate, dedicated thread.

The object is being serialized as it’s handed to the worker, and then, de-serialized on the other end. The page and worker do not share the same instance, so the end result is a duplicate created on each pass. Most browsers implement this feature by automatically JSON encoding/decoding the value on either end.

To set up two-way communication, both the main page and the worker thread listen for the onmessage event.

For example, when a button is pressed, a message is sent to the Worker that then echos it back. The message is passed in using JSON and then back from the Worker as JSON too.

Here is the HTML.

Here is the JavaScript in a file named 3-TwoWay.js.

Simple Example

The following example tries to calculate Pi. Here’s the html:

Here is the worker.

More Web Worker Features

There are additional ways to use Web Workers. Let your creativity begin.

Inline

You may want to create the Worker inline inside the web page instead of inside a separate file. Or you have may a need to provide the code by some external source as a string. In these cases you can use inline Web Workers.

Here is an example script tag.

The main way to create an inline Web Worker is using the BlobBuilder object which was added by the HTML5 File API. BlobBuilder enables you to create a blob object from a given string.

For more information, see Working with Inline Web Workers.

Shared Workers

Shared web workers allow any number of scripts to communicate with a single worker.

To create a shared web worker, you pass a JavaScript file name to a new instance of the SharedWorker object and then you communicated via a port object and attach a message event handler.

See How to Use JavaScript Shared Web Workers in HTML5 for examples.

ShareWorkers not supported in all modern browsers.

Subworkers

Workers may spawn more workers if they wish.  So-called subworkers must be hosted within the same origin as the parent page.  Also, the URIs for subworkers are resolved relative to the parent worker’s location rather than that of the owning page.  This makes it easier for workers to keep track of where their dependencies are.

Subworkers are not currently supported in all browsers.

Importing Scripts

Worker threads have access to a global function, importScripts() , which lets them import scripts or libraries into their scope.  It accepts as parameters zero or more URIs to resources to import, such as:

Online Examples

For hands-on demonstrations of Web Workers in action, see Web Worker Fountains and Web Worker Harness for test262 on the IE Test Drive.

Mozilla developer page shows Advanced passing JSON Data and creating a switching system. The demos shows how you can pass some complex data and have to call many different functions both in the main page and in the Worker

Sample code is provided in the Web Worker specification for these scenarios:

  • A background number-crunching worker. The simplest use of workers is for performing a computationally expensive task without interrupting the user interface.
  • Worker used for background I/O. The main document uses two workers, one for fetching stock updates for at regular intervals, and one for fetching performing search queries that the user requests.
  • Shared workers introduction. Shared workers use slightly different APIs, since each worker can have multiple connections.
  • Shared state using a shared worker. Multiple windows (viewers) can be opened that are all viewing the same map.
  • Delegation. A computationally expensive task that is to be performed for every number from 1 to 10,000,000 is farmed out to ten subworkers.

Debugging

IE10 offers you to directly debug the code of your Web Workers inside its script debugger like any other script.

For that, you need to launch the development bar via the F12 key and navigate to the “Script” tab. You shouldn’t see the JS file associated to your worker yet. But right after pressing the “Start debugging” button, it should magically be displayed:

F12DebugWorker001

Next step is then to simply debug your worker like you’re used to debug your classic JavaScript code.

F12DebugWorker002

References

For more information about Web Workers, see:

Sample Code

Sample code is available in the DevDays GitHub repository. See https://github.com/devdays/html5-tutorials

Advertisements