29 Jun 2010: Added links to Part 5 of this series in About this series, Summary, and Resources sections.
HTML 5 is a very hyped technology, but with good reason. It promises to be a technological tipping point to bring desktop application capabilities to the browser. As promising as it is for traditional browsers, it has even more potential for mobile browsers. Even better, the most popular mobile browsers have already adopted and implemented many significant parts of the HTML 5 specification. In this five-part series, you will take a closer look at several of those new technologies that are part of HTML 5 and can have a huge impact on mobile Web application development. In each part, you will develop a working mobile Web application showcasing an HTML 5 feature that you can use on modern mobile Web browsers like the ones found on the iPhone and Android-based devices.
In this article, you will develop Web applications using the latest Web technologies. Most of the code here is just HTML, JavaScript, and CSS—the core technologies of any Web developer. The most important thing you will need are browsers to test things on. Most of the code in this article will run on the latest desktop browsers with some noted exceptions. Of course, you must test on mobile browsers too, and you will want the latest iPhone and Android SDKs for those. In this article, iPhone SDK 3.1.3 and Android SDK 2.1 were used. This article's sample also uses a proxy server to access remote services from the browser. The proxy server is a simple Java™ servlet, but could be easily replaced by a proxy in PHP, Ruby, or others. See Resources for links.
Multi-threaded JavaScript on mobile devices
Multi-threaded or concurrent programming is nothing new to most developers. It is supported, in one way or another, in most modern programming languages. However, JavaScript is not one of the languages that supports concurrent programming. Its creator thought that it was too problematic and unnecessary for a language like JavaScript that was designed for performing simple tasks on Web pages. However, as Web pages have evolved into Web applications, the complexity level of tasks done with JavaScript has grown to put JavaScript on par with any other language. At the same time, developers working in other languages that support concurrent programming have often suffered and struggled with the extraordinarily high complexity that comes along with concurrent primitives like threads and mutexes. In fact, recently, a number of new languages like Scala, Clojure, and F# have evolved, all promising to simplify concurrency.
The Web Worker specification is not just about adding concurrency to JavaScript and Web browsers; it is about doing this in a smart way that will empower developers without giving them a tool that causes problems. For example, desktop application developers have used multi-threading for years to let their applications access I/O resources without freezing the UI while they wait for these resources. However, such applications often suffer when these multiple threads change shared resources (including the UI) which leads to applications that freeze up or crash. With Web Workers, this cannot happen. The spawned thread does not have access to the same resources as the main UI thread. In fact, the code in the spawned thread cannot even be in the same file as the code that is executed by the main UI thread.
You even have to supply that external file as part of the constructor, as in Listing 1.
This process uses three things:
- The JavaScript for the Web page (I will call it the page script) executed on the main thread.
- The Worker object which is the JavaScript object that you use to perform the Web Worker function.
- The script that will be executed on the newly-spawned thread. I will call it the Worker script.
Let's start by looking at the page script in Listing 1.
Listing 1. Using a Web Worker in the page script
var worker = new Worker("worker.js");
worker.onmessage = function(message){
// do stuff
};
worker.postMessage(someDataToDoStuffWith);
|
In Listing 1, you can see the three basic steps in using Web Workers. First, you create a Worker object and pass it the URL of the script that will be executed in the new thread. All of the code that the Worker will execute must be contained in the Worker script whose URL is passed in to the constructor of the Worker. The URL of this Worker script is limited by the browser's same origin policy—it must come from the same domain that loaded the page that loaded the page script that is creating the Web Worker.
The next thing you do is to specify a callback handler function using the onmessage function. This callback function will be invoked after the Worker
script executes. message is data that is returned
from the Worker script and you can do whatever you want with that message. The
callback function is executed on the main thread, so it has access to the DOM. The Worker
script runs in a different thread and does not have access to the DOM, so you need to
send data from the Worker script back to the main thread where you can safely modify
the DOM to update the UI of your application. This is the key feature of the
nothing-shared design of Web Workers.
The last line of Listing 1 shows how the Worker is initiated—by calling its postMessage function. Here you pass a
message (again, this is just data) to the Worker. Of course, postMessage is an asynchronous function; you call it and it
immediately returns.
Now, examine the Worker script. The code in Listing 2 is the contents of the worker.js file from Listing 1.
Listing 2. A Worker script
importScripts("utils.js");
var workerState = {};
onmessage = function(message){
workerState = message.data;
// do stuff with the message
postMessage({responseXml: this.responseText});
}
|
You can see that the Worker script has its own onmessage
function. This is invoked when you call postMessage from
the main thread. The data you passed from the page script is passed to the postMessage function in the message object.
You access the data by retrieving the data property of the
message object. When you finish processing the data in the Worker
script, you invoke the postMessage function to send data back to the main thread. That data is also available to the main thread by accessing the data property of the message it receives.
So far you have seen the simple, but powerful semantics of Web Workers. Next, you will see how to apply this to speed up mobile Web applications. Before getting into that, it's necessary to talk about device support. After all, these are mobile Web applications and dealing with the differences in capabilities between browsers is essential to mobile Web application development.
Starting with Android 2.0, the Android browser has had full support for the HTML 5 Web Worker specification. At the time of writing this article, most new Android devices, including the very popular Motorola Droid, are shipping with Android 2.1. In addition, this feature is fully supported in the Mozilla Fennec browser available on Nokia devices running its Maemo operating system, and on Windows Mobile devices. The notable omission here is the iPhone. iPhone OS versions 3.1.3 and 3.2 (the versions of the OS that run on the iPad) do not support Web Workers yet. However, this feature is already supported on Safari, so it should just be a matter of time before it shows up on the Mobile Safari browser running on the iPhone. Given the dominance of the iPhone (especially in the US) it is best to not rely on Web Workers being present and only use them to enhance your mobile Web applications when you detect that they are present. With this in mind, look at how you can use Web Workers to make mobile Web applications faster.
Improving performance with Workers
Web Worker support on smart phone browsers is good and improving. This begs the question: When do you want to use Workers in mobile Web applications? The answer is simple: whenever you need to do something that takes a long time. Some Worker examples show Workers being used to perform intense mathematical computations, like calculating ten-thousand digits of pi. It's fairly unlikely that you will ever need to perform such a computation on any Web application, much less a mobile Web application. However, retrieving data from remote resources is fairly common, and that is the focus of the example in this article.
In this example, you will retrieve a list of Daily Deals (deals that change each day)from eBay. The deals list contains brief information about each deal. More detailed information can be obtained by using eBay's Shopping API. You will use Web Workers to pre-fetch this additional information while the user browses the list of deals to pick the one of interest. To access all of this eBay data from your Web application, you will need to deal with the browser's same origin policy by using a generic proxy. A simple Java servlet was used for this proxy. It is included with the code for this article, but it is not shown here. Instead, let's focus on the code that works with the Web Workers. Listing 3 shows the basic HTML page for the deals application.
Listing 3. Deals application HTML
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name = "viewport" content = "width = device-width">
<title>Worker Deals</title>
<script type="text/javascript" src="common.js"></script>
</head>
<body onload="loadDeals()">
<h1>Deals</h1>
<ol id="deals">
</ol>
<h2>More Deals</h2>
<ul id="moreDeals">
</ul>
</body>
</html>
|
As you can see, this is a very simple piece of HTML; it is just a shell. You retrieve
the data and generate the UI using JavaScript. This is the optimal design for mobile
Web applications, since it allows for all of the code and static markup to be cached
on the device, and the user only waits for data from the server. Notice that, in Listing 3, once the body has loaded, you call the loadDeals function where you load the initial data for the
application in Listing 4.
Listing 4. The
loadDeals function
var deals = [];
var sections = [];
var dealDetails = {};
var dealsUrl = "http://deals.ebay.com/feeds/xml";
function loadDeals(){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if (this.readyState == 4 && this.status == 200){
var i = 0;
var j = 0;
var dealsXml = this.responseXML.firstChild;
var childNode = {};
for (i=0; i< dealsXml.childNodes.length;i++){
childNode = dealsXml.childNodes.item(i);
switch(childNode.localName){
case 'Item':
deals.push(parseDeal(childNode));
break;
case "MoreDeals":
for (j=0;j<childNode.childNodes.length;j++){
var sectionXml= childNode.childNodes.item(j);
if (sectionXml && sectionXml.hasChildNodes()){
sections.push(parseSection(sectionXml));
}
}
break;
default:
break;
}
}
deals.forEach(function(deal){
var entry = createDealUi(deal);
$("deals").appendChild(entry);
});
loadDetails(deals);
sections.forEach(function(section){
var ui = createSectionUi(section);
$("moreDeals").appendChild(ui);
loadDetails(section.deals);
});
}
};
xhr.open("GET", "proxy?url=" + escape(dealsUrl));
xhr.send(null);
}
|
Listing 4 shows the loadDeals function, and
the global variables used in the application. You use an array of deals plus an array
of sections. These are additional groups of related deals (for example, Deals under $10). There is also a map called dealDetails whose keys will be Item IDs (which you will get from the
deals data), and whose values are the more detailed information obtained from the eBay Shopping API.
The first thing you do is make an Ajax call to a proxy, which in turn calls the eBay
Daily Deals REST API. This gives you the list of deals as an XML document. You parse
the document in the onreadystatechange function of the
XMLHttpRequest object used to make the Ajax call. You use two other functions, parseDeal and parseSection, to parse the
XML nodes into easier-to-use JavaScript objects. You can find these functions in the
downloadable code sample (see Downloads), but they are just
tedious XML parsing functions so I didn't include them here. Finally, after parsing
the XML, you use two more functions, createDealUi and createSectionUi, to modify
the DOM. When you are done, the UI looks like Figure 1.
Figure 1. Mobile Deals UI
If you go back to Listing 4, notice that after the primary deals are
loaded, you call the loadDetails function for each of the
sections of deals. In this function, you load the additional details for each deal by
using the eBay Shopping API—but only if the browser supports Web Workers. Listing 5 shows the loadDetails function.
Listing 5. Pre-fetching deals details
function loadDetails(items){
if (!!window.Worker){
items.forEach(function(item){
var xmlStr = null;
if (window.localStorage){
xmlStr = localStorage.getItem(item.itemId);
}
if (xmlStr){
var itemDetails = parseFromXml(xmlStr);
dealDetails[itemDetails.id] = itemDetails;
} else {
var worker = new Worker("details.js");
worker.onmessage = function(message){
var responseXmlStr =message.data.responseXml;
var itemDetails=parseFromXml(responseXmlStr);
if (window.localStorage){
localStorage.setItem(
itemDetails.id, responseXmlStr);
}
dealDetails[itemDetails.id] = itemDetails;
};
worker.postMessage(item.itemId);
}
});
}
}
|
In loadDetails, you first check for the Worker function in the global scope (the window object.) If it is not there, then you simply do nothing. If it
is there, then you first check the localStorage for the XML for this deal's details. This is a local caching strategy common to mobile Web applications, and is described in detail in Part 2 of this series (see Resources for a link).
If the XML is found locally, then you parse it in the parseFromXml function and add the details
to the dealDetails object. If it is not found locally, then spawn a Web
Worker and send it the Item ID of the deal using postMessage. Once that Worker retrieves the data and posts back to
the main thread, you parse the XML, add the result to dealDetails, and store the XML
in the localStorage. Listing 6 shows the Worker script, details.js.
Listing 6. Deal details Worker script
importScripts("common.js");
onmessage = function(message){
var itemId = message.data;
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if (this.readyState == 4 && this.status == 200){
postMessage({responseXml: this.responseText});
}
};
var urlStr = generateUrl(itemId);
xhr.open("GET", "proxy?url=" + escape(urlStr));
xhr.send(null);
}
|
The Worker script is pretty simple. You use Ajax to call the proxy, which in turn calls
the eBay Shopping API. Once you receive the XML from the proxy, you send it back to
the main thread using a JavaScript object literal. Note that, even though you can use
XMLHttpRequest from a Worker, everything will come back on
its responseText property, never its responseXml property. That is because there is no JavaScript DOM
parser in scope within the Worker script. Note that the generateUrl function comes from the common.js file (in Listing 7). You imported common.js using the importScripts function.
Listing 7. Script imported by the Worker
function generateUrl(itemId){
var appId = "YOUR APP ID GOES HERE";
return "http://open.api.ebay.com/shopping?callname=GetSingleItem&"+
"responseencoding=XML&appid=" + appId + "&siteid=0&version=665"
+"&ItemID=" + itemId;
}
|
Now that you have seen how to populate the deal details (for browsers that support Web Workers), go back to Figure 1 to figure out how this is used in the application. Notice that each deal has a Show Details button next to it. Click it to modify the UI as in Figure 2.
Figure 2. Deal details displayed
This UI is displayed when you invoke the showDetails
function. Listing 8 shows this function.
Listing 8. The showDetails function
function showDetails(id){
var el = $(id);
if (el.style.display == "block"){
el.style.display = "none";
} else {
el.style.display = "block";
if (!el.innerHTML){
var details = dealDetails[id];
if (details){
var ui = createDetailUi(details);
el.appendChild(ui);
} else {
var itemId = id;
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if (this.readyState == 4 &&
this.status == 200){
var itemDetails =
parseFromXml(this.responseText);
if (window.localStorage){
localStorage.setItem(
itemDetails.id,
this.responseText);
}
dealDetails[id] = itemDetails;
var ui = createDetailUi(itemDetails);
el.appendChild(ui);
}
};
var urlStr = generateUrl(id);
xhr.open("GET", "proxy?url=" + escape(urlStr));
xhr.send(null);
}
}
}
}
|
You are passed the ID of the deal that is going to be shown and toggle whether it is displayed or not. When it is first called, it checks to see if the details have already been stored in the dealDetails map. If the browser supports Web Workers, then these details are already stored there and the UI for it is created and added to the DOM. If the details are not loaded already, or if the browser does not support Workers, then you make an Ajax call to load this data. This is how the application can work equally well whether Workers are present or not. This means that, if Workers are supported, then the data is already loaded and the UI will respond instantly. If there are no Workers, the UI still loads, but it takes a few seconds.
Web Workers sound like an exotic new technology for Web developers. But, as you have seen in this article, they have very practical applications. This is especially true for mobile Web applications. The Workers can be used to prefetch data or perform other ahead-of-time operations to provide a much more lively UI. This can be especially true on mobile Web applications that need to load data over a potentially slow network. Combine this with caching strategies, and your users will be amazed by the snappiness of your application.
| Description | Name | Size | Download method |
|---|---|---|---|
| Article source code | Workers.zip | 8KB | HTTP |
Information about download methods
Learn
- Creating mobile Web applications with HTML 5, Part 1: Combine HTML 5, geolocation APIs, and Web services to create mobile mashups (Michael Galpin, developerWorks, May 2010): Find and track location coordinates to use in various Web services. Use the various aspects of the geolocation standard with HTML 5 and popular Web services to create an interesting mobile mashup in Part 1 of this series.
- Creating mobile Web applications with HTML 5, Part 2: Unlock local storage for mobile Web applications with HTML 5 (Michael Galpin, developerWorks, May 2010): Explore an important new feature in HTML 5 for wireless applications. With the standardization of local storage and a simple API, store large amounts of data on the client and improve performance.
- Creating mobile Web applications with HTML 5, Part 3: Make mobile Web applications work offline with HTML 5 (Michael Galpin, developerWorks, June 2010): Enable your application to function with or without an Internet connection and learn to detect when your application goes from offline to online and vice versa.
- Creating mobile Web applications with HTML 5, Part 5: Develop new visual UI features in HTML 5: Add Canvas, CSS3, and more semantic elements to mobile Web apps (Michael Galpin, developerWorks, June 2010): Provide full 2-D graphics in the browser with Canvas, the most eye-catching of the new UI capabilities in HTML 5. Learn to use Canvas and other visual elements in your mobile Web applications.
- HTML 5 Offline Application Cache documentation: Take a look at Apple's more detailed information on manifest files and application cache.
- Offline resources in Firefox: Offline is not just for mobile. Read about how Firefox implements this standard in Mozilla.
- Create Ajax applications for the mobile Web (Michael Galpin, developerWorks, March 2010): Explore how to use Ajax, a key part of any mobile Web application.
- New elements in HTML 5 (Elliotte Rusty Harold, developerWorks, August 2007): HTML 5 is not just about JavaScript. Read about some of the new markup in HTML 5.
- Android and iPhone browser wars, Part 1: WebKit to the rescue (Frank Ableson, developerWorks, December 2009): Do you like the mobile Web application approach using HTML 5 approach, but still want your application in the iPhone App Store and Android Market? See how you can get the best of both worlds in Part 1 of this two-part article series.
- Dive Into HTML 5: Check out this free book for a great look at HTML 5 detection techniques as well as the many features of HTML 5.
- Safari Reference Library: Keep this resource handy if you develop Web applications for the iPhone.
- W3C HTML 5 Specification (Working Draft, March 2010): Explore this definitive source on HTML 5.
- More articles by this author (Michael Galpin, developerWorks, April 2006-current): Read articles about XML, Eclipse, Apache Geronimo, Ajax, more Google APIs, and other technologies.
- My developerWorks: Personalize your developerWorks experience.
- IBM XML certification: Find out how you can become an IBM-Certified Developer in XML and related technologies.
- XML technical library: See the developerWorks XML Zone for a wide range of technical articles and tips, tutorials, standards, and IBM Redbooks.
- developerWorks technical events and webcasts: Stay current with technology in these sessions.
- developerWorks on Twitter: Join today to follow developerWorks tweets.
- developerWorks
podcasts: Listen to interesting interviews and discussions for software developers.
Get products and technologies
-
Modernizr project is a comprehensive utility for detecting HTML 5 features.
- Modernizr project: Get a comprehensive utility for detecting HTML 5 features including like localStorage, Web Workers, applicationCache and others.
- The Android Developers Web site: Download the Android SDK, access the API reference, and get the latest news on Android.
- iPhone SDK: Get the latest iPhone SDK to develop iPad, iPhone and iPod touch applications.
- The Android Open Source Project: Get the open source code for the Android mobile platform.
- The Google App Engine SDK: Download Java and Python tools to build scalable Web applications using Google.
- IBM product evaluation versions: Download or explore the online trials in the IBM SOA Sandbox and get your hands on application development tools and middleware products from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.
Discuss
- Participate in the discussion forum.
- XML zone discussion forums: Participate in any of several XML-related discussions.
- developerWorks blogs: Check out these blogs and get involved.

Michael Galpin is an architect at eBay. He is a frequent contributor to developerWorks. He has spoken at various technical conferences, including JavaOne, EclipseCon, and AjaxWorld. To get a preview of what he is working on next, follow @michaelg on Twitter.



