Posted on by & filed under Content - Highlights and Reviews, Programming & Development.

A guest post by Craig Buckler, a freelance web developer from the UK and the lead technical writer at SitePoint.com who creates video tutorials for InfiniteSkills. Contact Craig at @craigbuckler.

Your web browser and the web page you’re viewing share a single processing thread. When an event occurs within the page or browser interface, the handler is added to a single queue. The next time the browser becomes idle, the next handler on the queue is processed.

In essence, the browser cannot do anything else when your JavaScript code is running; application menus and toolbars become inactive. Admittedly, it’s not quite that simple – browsers such as IE and Chrome start new operating system processes for every tab you open – but the single thread concept remains. It’s essential because JavaScript can interrupt browser processing and make changes to the HTML before, during or after the page has downloaded. In this post I will describe how to handle long-running processes in JavaScript with the help of Web Workers.

Browsers will not permit your script to run for too long since it prevents the user interacting with the application. Problems can therefore occur if you have a long-running or process-intensive task. Consider this simple example:

It performs one billion simple operations but is unlikely to get that far…

webworker1

How do Browsers Detect Unresponsive Scripts?

The vendors use different methods to assess browser responsiveness:

  • IE limits execution to a number of statements
  • Firefox and Safari limit execution to a number of seconds
  • Chrome and Opera 15+ detect when the browser becomes unresponsive

You cannot accurately determine when an “unresponsive script” error will be fired because computer processing speeds, operating systems and browsers vary. I would suggest any code that runs for more than a second on an average PC is at risk.

Application Refactoring

Ideally, web-based applications should not use long-running scripts. Web pages are event-driven; the user performs an action and the client-side code reacts. If your application requires intensive processes, there may be a better solution such as pre-calculation or offloading to the server. If that is not an option, you may need to consider threading.

What is Threading?

A thread is a separate process normally managed by your operating system. Modern operating systems are inherently multi-threaded, since you can run more than one application at a time. Each program can perform its own tasks without waiting for another to stop.

Multi-core processors can run more than one thread concurrently but hundreds of threads could be required. Therefore the OS switches between different processes; a thread is executed for a short time before switching to the next one.

Web Workers

Web Workers are a threading technology available in HTML5. If you normally develop desktop applications, you should be aware that JavaScript imposes rigorously-controlled restrictions:

  • Web Worker code must be available on the same origin (domain) as the parent process that calls them.
  • Web Worker threads operate independently of the browser UI; they cannot access or modify the page DOM.
  • Web Workers cannot access global variables or JavaScript functions within your main page.
  • Access to some browser objects is restricted, e.g. window.location properties are read-only.
  • Web Workers may only communicate with your main page by receiving and returning a single parameter – although that can be an object or an array containing many items.
  • Web Workers are event-driven; you must write code to respond to asynchronous events in both your calling application and the Web Worker code.

Web Workers are supported in Firefox, Chrome, Safari, Opera and IE10+. We’ll re-write our large loop program to use Web Workers:

This code performs the following actions:

  1. First, we check if Web Workers are supported in the browser by checking for the existence of the window.Worker object.
  2. The Web Worker thread code is then loaded from a separate JavaScript file using new Worker(filename).
  3. A new handler function is defined for the onmessage event. This is executed when the Web Worker completes its work and returns an object, e. The data property is the returned message.
  4. Finally, we start the Web Worker thread with the postMessage method. In this code, we’re passing the size of the loop as an argument.

Our Web Worker code is now defined in webworker.js (the filename we used above):

This implements a single event handler that waits for a message sent from the calling application when it executes the postMessage method.

  1. The function is passed a message object, e, which contains our parameter (999999999) in the data property.
  2. The loop then runs within the background process.
  3. Finally, we can return the processing result to the calling application using another postMessage method call. This runs the onmessage handler function defined in the page above.

You will not experience any “unresponsive script” errors because webworker.js runs as a background process. As a bonus, the script generally executes faster because the browser runs it in a more limited context which is not able to communicate with the DOM.

There are a number of further points you should note about Web Workers:

  • Web Workers can import other scripts using the importScripts(“filename”) command. Multiple files can be imported by separating each name string with a comma.
  • You can spawn any number of Web Workers; each can spawn any number of sub-workers and so on. The script URI for sub-workers is relative to the parent worker’s location rather than the root page.
  • If you need to stop a Web Worker from your main page, you can use the terminate() method, e.g. thread.terminate();.
  • Web Workers may terminate themselves using the close() method, e.g. self.close();.
  • If an error occurs within a Web Worker, an onerror handler is called and passed an error event object, which exposes properties including filename, message and lineno:

Pseudo-Threading

Web Workers are not supported in older browsers. While shims are available, they only implement the Web Worker API and cannot execute background processes. “Unresponsive Script” errors could still occur.
As an alternative, you could consider using JavaScript timers such as setTimeout and setInterval:

This runs myfunction() after approximately 50 milliseconds but, importantly, the browser is freed to perform other tasks during that time.

If we need to run a series of process-intensive functions, we can add a delay between them:

These functions, however, may not be called in the order you expect. To rectify that issue, we can create a function to handle calling in sequence:

Using timers is not perfect since a single function could result in an “Unresponsive Script” error, however, this may be the best option if support for older browsers is essential.

For more tutorials about Web Workers and other HTML5 APIs, try my Learning JavaScript Programming video course.

Be sure to look at the JavaScript resources below that you can find in Safari Books Online.

Not a subscriber? Sign up for a free trial.

Safari Books Online has the content you need

Learning JavaScript Programming Video teaches you the basics of programming with JavaScript, the worlds most used programming language. The tutorial is designed for the absolute beginner – no prior JavaScript programming experience is required in order to get the most out of this video training. You will start with learning what programming is, and specifically, what JavaScript is, how it it used, and its limitations. You will discover variables and data types, and how to take input and create output. Craig covers conditions, loops, arrays, sorting, functions, paramaters and debugging. You will even learn advanced concepts such as OOP, string manipulations, regular expressions and other programming patterns.
Eloquent JavaScript is a guide to JavaScript that focuses on good programming techniques rather than offering a mish-mash of cut-and-paste effects. The author teaches you how to leverage JavaScript’s grace and precision to write real browser-based applications. The book begins with the fundamentals of programming—variables, control structures, functions, and data structures—then moves on to more complex topics, like object-oriented programming, regular expressions, and browser events. With clear examples and a focus on elegance, Eloquent JavaScript will have you fluent in the language of the web in no time.

About the author

craigbuckler Craig Buckler is a freelance web developer from the UK. Having started in IT, Craig migrated to the web in the mid-1990’s and has worked on a variety of successful award-winning projects for large and small organizations. He is the lead technical writer at SitePoint.com and creates video tutorials for InfiniteSkills. Contact Craig at @craigbuckler.

Tags: Chrome, Firefox, html5, IE, Javascript, multi-core, multi-threaded, Opera, Safari, threading, unresponsive scripts, Web Workers,

Comments are closed.