Building faster pages with Web Workers

In recent years web pages and applications have been requiring the use of more, and increasingly complicated, JavaScript. Google Drive, for example, is a full blown desktop application which relies on JavaScript being responsive.

Although JavaScript performance has improved, large, complex scripts can still slow browsers down, or even cause them to freeze.

This is where Web Workers come in. They tell the browser to execute large, potentially problematic, scripts in the background so the user doesn’t have to deal with unresponsive pages. 

The limitations of JavaScript

JavaScript usually executes in a single thread, which means the browser works its way through the code, executing each statement at a time. The problem with this is that if a task takes a long time to complete it will freeze the browser, making the page unresponsive.

So what can Web Workers do to help? Because they provide a means to create new threads, Web Workers allow scripts to be written with a multi-threaded architecture.

Working this way, with multiple threads, ensures the page remains responsive even if there is a large chunk of Javascript to process.

Creating Web Workers 

Creating a Web Worker is quite a simple task. Place the script you want a Worker to execute in a separate .js file, then simply create a new Worker object containing the path of the .js file: 

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

Providing the path is correct, the browser will start a new Worker thread which will download asynchronously. If not, the worker will throw a silent error. 

The Worker is started by calling the postMessage() method:

worker.postMessage();

Communicating with a Worker

Now you know how to spawn a new Web Worker, the next step is how to pass data between the Worker and the main application. This is done using the postMessage API to pass strings or JavaScript objects from one to the other:

worker.postMessage(‘This is the main application’);

This sends the enclosed string from the main application to the Worker, but in order for the Worker to ‘hear’ the message, we need to set up some event listeners in the Worker script file:

self.addEventListener(‘message’, function(e) {
self.postMessage(‘Main said: ‘ + e.data);
}, false);

This tells the Worker to ‘listen’ for a message, then prepends ‘Main said:’ to the received message string and sends this back to the main application using self.postMessage().

Now the main application file needs an event listener to receive the message sent to it from the Worker:

worker.addEventListener(‘message’, function(e) {
alert(e.data);
}, false);

This sets up the main application to receive a message from the Worker, and to display the ‘conversation’ as a browser alert:

This is the main application.
Main said: This is the main application.
Main said: This is the main application.

Terminating Workers 

Once the Worker has finished its task it should be terminated. This can be done either in the main application:

worker.terminate();

or they can terminate themselves by calling the close function:

self.close();

Security and restrictions

Web Workers can be really helpful when building applications that rely heavily on Javascript, but they do have some limitations: 

  • The Worker scripts must be served from the same domain as the main application.
  • The Worker script runs outside the main file and so does not have access to the DOM.
  • A Web Worker will not work if called from inside a file system, so testing locally requires a server set up like LAMP or WAMP to be installed.

Conclusion

HTML5 continues to evolve and pleasantly surprise us. With Web Workers we have a way of creating large JavaScript heavy applications without the worry that the browser will not be able to cope.

The concept has been around for a couple of years, but unfortunately it hasn’t been used to it’s full potential yet.

 

Have you used Web Workers? Is your JavaScript growing? Let us know in the comments.