Five Ajax Anti-patterns
时间:2007-08-18 来源:linxh
Level: Intermediate
Jack D Herrington ([email protected]), Senior Software Engineer, Leverage Software Inc.
20 Mar 2007
You can learn a lot about how to do things correctly by understanding how things are done incorrectly. Certainly, there's a right way and a wrong way to write Asynchronous JavaScript™ + XML (Ajax) applications. This article discusses some common coding practices you will want to avoid.If people did everything right the first time, the world would be entirely different. So it is with Ajax. I've done a lot of coding, writing, talking, and supporting of Ajax developers, including myself. And through that I've learned a lot about how Ajax is done right and how it's done wrong. In my last article, Five common Ajax patterns: Helpful Ajax design patterns you can use today, I presented five patterns for writing Ajax applications correctly. In this article, I present five anti-patterns that I often see in Ajax code.
What is an anti-pattern, you might ask? An anti-pattern is an application design flaw that's seen often enough to be an issue everyone should watch out for. I'm talking high level here. Syntax errors and linker problems need not apply.
Most developers have heard of a good example of an anti-pattern: The improper use of Structured Query Language (SQL) libraries that result in SQL injection attacks on Web sites. That anti-pattern has cost companies lost revenue, exposed customer records, and unfortunately is achievable in every programming language. So, it's worth understanding how and why it happens and how to avoid it.
So it is with these Ajax anti-patterns. Now, I'm not saying that they can cost companies billions in lost revenues. But they can crash servers or provide poor customer experience, and that can be both frustrating and expensive.
You can learn a lot if you understand what can go wrong. Too often, people think that Ajax is just a way to fetch XML from the server after a page has loaded. That's a very limited view, and it can cause performance problems in applications if it's misapplied. In this article, I present both why it's wrong and how to fix it.
Polling on a timer when you don't need to
Many of the Ajax issues I see have to do with misuse of the timer functionality built into the JavaScript language. The key method is window.setInterval(). Whenever you see that method, it should set off a little alarm in your mind; why is this person using a timer? Certainly, timers have their purpose -- animations, for example.
The window.setInterval() method tells the page to call back a particular function on a particular interval -- say, every second. Most browsers talk a good game when it comes to these timers, but they rarely deliver, primarily because the JavaScript language is single threaded. If you ask for a second, you might get the callback at 1 second or 1.2 seconds or 9 seconds or any other time.
One place a timer is certainly not necessary is to watch for an Ajax request to finish. Take the example in Listing 1.
Listing 1. Antipat1a_polling.html
<html><body> Dynamic content is shown between here:<br/> <div id="htmlDiv" style="border:1px solid black;padding:10px;"> </div>And here.</body></html> |
Everything looks pretty good until you get to the setInterval call. This call sets a timer that will watch the status of the request, and then sets the content of the page from the downloaded material.
I'll show a better solution to the problem of hoe to figure out when a request is complete shortly. In the meantime, Listing 2 shows the file the page is requesting.
Listing 2. Antipat1_content.html
<b>Hello there</b> |
And Figure 1 shows the page as it's seen in my browser.
Figure 1. The content placed in the HTML document 500)this.width=500;" border=0>
How pretty. From the look of it, this page makes a lot of sense. When I change the search text, the result area changes based on the new criteria (well, not really, but if I had a real search engine behind the request, it would.)
The problem is that the JavaScript code is using window.setInterval to keep making the request over and over, even when the content of the search field hasn't changed. That eats up network bandwidth, and it eats up server time. On popular sites, that can be a deadly combination.
The solution is to use the event callbacks on the search box, as shown in Listing 5.
Listing 5. Antipat1b_fixed.html
<html><body><form> Search <input id="searchText" type="text" onkeyup="runSearch()">:<br/> <div id="htmlDiv" style="border:1px solid black;padding:10px;"> </div></form></body></html> |
Here, I hooked the runSearch() function onto the onkeyup() method of the search box. That way, I get called back when the user types something into the search box.
What runSearch() does is pretty nifty. It sets a single timeout for a second way that will call the server and actually run the search. And it clears that timeout if it hasn't yet elapsed before setting it. Why? Because that allows for the user to type a lot of text; then, a second after the user presses the last key, the search will run. That way, the user isn't bothered by a continuously flickering display.
|
Not inspecting the return results in the callback
Many Ajax anti-patterns spring from misunderstanding the mechanism of the XMLHTTPRequest object. One that I often see is when users don't inspect the readyState or status fields of the object in the callback. Have a look at Listing 6 to see what I mean.
Listing 6. Antipat2_nocheck.html
<html></head><body> <table cellspacing="0" cellpadding="3" width="100%"><tbody id="dataBody"> <tr> <th width="20%">Year</th> <th width="80%">Title</th> </tr> </tbody></table></body></html> |
This code reads data from the XML file shown in Listing 9 and formats it into a table.
Listing 9. Antipat3_data.xml
<movies> <movie> <year>1993</year> <title>Jurassic Park</title> </movie> <movie> <year>1997</year> <title>The Lost World: Jurassic Park</title> </movie> <movie> <year>2001</year> <title>Jurassic Park III</title> </movie> </movies> |
You can see the result in Figure 3.
Figure 3. The complex movie listings page
500)this.width=500;" border=0>This is not bad code at all. It's just a lot of code to perform what is actually a relatively simple task. The resulting page isn't complex at all. It can't be sorted or searched on the client side. In fact, there is almost no reason to go through this complex conversion between XML and HTML.
Wouldn't it be simpler to have the server return HTML instead of XML, as in Listing 10?
Listing 10. Antipat3_fixed.html
<html><body><div id="tableDiv"></div></body></html> |
And indeed, it is simpler. All the complex table row and cell creation code is replaced with a single set of the innerHTML of a <div> tag on the page. Voilà!
The HTML returned from the server is shown in Listing 11.
Listing 11. Antipat3_content.html
<table cellspacing="0" cellpadding="3" width="100%"> <tbody id="dataBody"> <tr> <th width="20%">Year</th> <th width="80%">Title</th> </tr> <tr> <td>1993</td> <td>Jurassic Park</td> </tr> <tr> <td>1997</td> <td>The Lost World: Jurassic Park</td> </tr> <tr> <td>2001</td> <td>Jurassic Park III</td> </tr> </tbody> </table> |
As with everything, the choice between whether to process on the server or on the client depends on the demands of the job. In this case, the job was relatively simple: Put up a table of movies. If the job were more complex -- perhaps with sorting, searching, adding or deleting, or dynamic interaction in which clicking a movie brings up more information -- then I can see going with more complex code on the client. In fact, I'll demonstrate sorting on the client at the end of this article, where I talk about the counter-argument of putting too great a load on the server.
Perhaps the perfect example of all of this is Google Maps. Google Maps does an elegant job of mixing rich-client side code with an intelligent mapping engine on the server side. I use that service as an example of how to determine what processing to do where.
|
Passing XML when you should pass JavaScript code
With all the hype about having Web browsers read XML data sources and dynamically render them, you might think it was the only method available. However, you would be wrong, because very smart engineers have used Ajax transport technology to send JavaScript code instead of XML. Take the version of the movie table example shown in Listing 12.
Listing 12. Antipat4_fixed.html
<html><head></head><body> <table cellspacing="0" cellpadding="3" width="100%"> <tbody id="dataBody"><tr> <th width="20%">Year</th> <th width="80%">Title</th> </tr></tbody></table></body></html> |
Instead of reading XML from the server, it reads JavaScript code. The code then uses the eval() function on the JavaScript code to get the data, which it can then quickly use to build the table.
The JavaScript data is shown in Listing 13.
Listing 13. Antipat4_data.js
[ { year: 1993, name: 'Jurassic Park' }, { year: 1997, name: 'The Lost World: Jurassic Park' }, { year: 2001, name: 'Jurassic Park III' } ] |
This functionality requires that you get the server to talk in the JavaScript language. But that's not usually a big deal. Most of the popular Web languages already support JavaScript Object Notation (JSON) output.
The benefits are clear. In this example, there was a 52% savings in the size of the data blob downloaded to the client by going with the JavaScript language. There's a performance increase, as well. It was 9% faster to read the JavaScript version. While 9% might not seem like a lot, just remember that this was a fairly rudimentary example. Larger blocks of data or more complex structures will require more XML parsing code, while the JavaScript code will remain unchanged.
|
A counter-argument to doing to little on the server is doing too much on the server. As I mentioned before, it's a balancing act. But I would like to show how to perform sorting of the movie table on the client as a demonstration of how to offload work from the server.
This sortable movie table is shown in Listing 14.
Listing 14. Antipat5_sort.html
<html><head></head><body> <table cellspacing="0" cellpadding="3" width="100%"> <tbody id="dataBody"><tr> <th width="20%"><a href="javascript: void runSort('year')">Year</a></th> <th width="80%"><a href="javascript: void runSort('name')">Title</a></th> </tr></tbody></table></body></html> |
This is a fairly simple example. It wouldn't work for particularly long lists of movies that would likely be shown in pages. But it does demonstrate that it's easy to put together a table that sorts quickly, without a page refresh and without bothering the server to do the hard, uninteresting work of the sort.
|
I've written many articles on Ajax and done a lot of Ajax work. I moderate the Ajax forum on IBM developerWorks. So, I know a thing or two about Ajax and how it's used and misused. One of the things I see most often is developers underestimating the complexity of Ajax by thinking that it's just about sending some XML, JavaScript, or HTML code to the browser. I look at the Ajax platform as the entire browser -- in fact, the entire set of popular browsers, because you have to know the quirks of all of them.
What it all comes down to is this: There's a lot about Ajax to learn, and there are a lot of mistakes to make along the way. Hopefully, this article will help you avoid some of the pitfalls or get out of them when you fall in. Either way, while there is a lot you can learn from your successes, there is often more that you can learn from your mistakes.
Learn
-
developerWorks XML zone: Learn all about XML at the developerWorks XML zone.
-
JSON home page: Visit the JSON page, a reference for passing JavaScript Object Notation data from the server to the client.
-
ECMA International site: Find the definitions for JavaScript, actually the language known as ECMAScript.
- AjaxPatterns: Explore this site dedicated to the topic.
-
Design Patterns (Erich Gamma, et al., Addison-Wesley, 1995): Read the seminal book on the use of patterns in software engineering.
-
Ajax Design Patterns (Michael Mahemoff, O’Reilly, 2006): Read this book, a new source specifically covering design patterns for Ajax.
-
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.
-
Ajaxian: With this great resource, keep up with developments in Ajax and the front-end widgets that use it.
Discuss
-
Participate in the discussion forum.
- XML zone discussion forums: Participate in any of several XML-centered forums.
Jack D. Herrington is a senior software engineer with more than 20 years of experience. He's the author of three books: Code Generation in Action, Podcasting Hacks, and PHP Hacks. He has also written more than 30 articles. You can reach Jack at [email protected]. |