Quantcast
Viewing latest article 1
Browse Latest Browse All 10

JavaScript Web Workers

This post is on a topic covered at one of Bold Innovation Group’s bi-weekly lunch and learn events where the developers here at Bold sit down and learn about a topic that might be new to them, or might be something of interest.

Web workers are an efficient way to improve the performance of your web application. They allow you to write asynchrous JavaScript which helps by not locking up the UI while the user is trying to do something which requires a lot of processing. Your app is only as fast as it looks to the user so if the UI is locking up, your users will think your app is no good.

Web workers might be over kill for some of the things you want to do but are very useful when building applications for the web that would traditionally be a desktop application.. now we can do it on the web. Web workers can be useful for:

  • 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 webservices
  • Processing large arrays or humungous JSON responses
  • Image filtering in a canvas
  • Updating many rows of a local web database

It is important to note that web workers do not have access to:

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

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! To run your app from the file:// scheme, run Chrome with the –allow-file-access-from-files flag set. NOTE: It is not recommended to run your primary browser with this flag set. It should only be used for testing purposes and not regular browsing.

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.

Workers

Web Workers run in an isolated thread. As a result, the code that they execute needs to be contained in a separate file. But before we do that, the first thing to do is create a new Worker object in your main page. The constructor takes the name of the worker script:

var worker = new Worker('task.js');

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.

After creating the worker, start it by calling the postMessage() method:

worker.postMessage(); // Start the worker.

example js

var worker = new Worker('doWork.js');
 
worker.addEventListener('message', function(e) {
  console.log('Worker said: ', e.data);
}, false);
 
worker.postMessage('Hello World'); // Send data to our worker.

doWork.js

self.addEventListener('message', function(e) {
  self.postMessage(e.data);
}, false);

 

Messages

Communication between a work and its parent page is done using an event model and the postMessage() method. Depending on your browser/version, postMessage() can accept either a string or JSON object as its single argument. The latest versions of the modern browsers support passing a JSON object.

When postMessage() is called from the main page, our worker handles that message by defining an onmessage handler for the message event. The message payload (in this case ‘Hello World’) is accessible in Event.data. Although this particular example isn’t very exciting, it demonstrates that postMessage() is also your means for passing data back to the main thread.

Messages passed between the main page and workers are copied, not shared.

doWork2.js

self.addEventListener('message', function(e) {
  var data = e.data;
  switch (data.cmd) {
    case 'start':
      self.postMessage('WORKER STARTED: ' + data.msg);
      break;
    case 'stop':
      self.postMessage('WORKER STOPPED: ' + data.msg +
                       '. (buttons will no longer work)');
      self.close(); // Terminates the worker.
      break;
    default:
      self.postMessage('Unknown command: ' + data.msg);
  };
}, false);

example js

function sayHI() {
  worker.postMessage({'cmd': 'start', 'msg': 'Hi'});
}
 
function stop() {
  // worker.terminate() from this script would also stop the worker.
  worker.postMessage({'cmd': 'stop', 'msg': 'Bye'});
}
 
function unknownCmd() {
  worker.postMessage({'cmd': 'foobard', 'msg': '???'});
}
 
var worker = new Worker('doWork2.js');
 
worker.addEventListener('message', function(e) {
  document.getElementById('example_result2').textContent = e.data;
}, false);

Error Handling

As with any programming you are going to want to handle errors. If an error occurs while a worker is executing, the an ErrorEvent is fired. The 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
  • message – a meaningful description of the error

This output makes it easy(ish) to debug your web worker.

workerWithError.js

self.addEventListener('message', function(e) {
  postMessage(1/x); // Intentional error.
};

example js

function onError(e) {
  document.getElementById('error').textContent = [
    'ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message
  ].join('');
}
 
function onMsg(e) {
  document.getElementById('result').textContent = e.data;
}
 
var worker2 = new Worker('workerWithError.js');
worker2.addEventListener('message', onMsg, false);
worker2.addEventListener('error', onError, false);
 
function startErrorWorker() {
  worker2.postMessage(); // Start worker without a message.
}

References / Open Source Projects

Bold Links

 

The post JavaScript Web Workers appeared first on Bold Apps Tech Blog.


Viewing latest article 1
Browse Latest Browse All 10

Trending Articles