KEMBAR78
High Performance Ajax Applications | PPT
High Performance Ajax Applications Julien Lecomte http://www.julienlecomte.net/blogfiles/performance/ajax-perf.ppt http://www.slideshare.net/julien.lecomte/high-performance-ajax-applications
Part 1 Developing For High Performance
Planning and designing for high performance Plan for performance from day 1 Work closely with designers and product managers Understand design rationale Explain the tradeoffs between design and performance Offer alternatives and show what is possible (prototype) Challenge yourself to implement challenging designs (don't just say no) Help simplify the design and interaction if needed (compromise)
Engineering high performance: A few basic rules Less is more Don’t do anything unnecessary. Don’t do anything until it becomes absolutely necessary. Break the rules Make compromises and break best practices, but only as a last resort! Work on improving perceived performance Users can deal with some reasonable amount of slowness if: They are informed appropriately that an operation is pending. The user interface remains reactive at all time. Cheat whenever you can by first updating the UI and then do the work. Need to “lock” all or part of the user interface.
Measuring performance Test performance using a setup similar to your users’ environment Profile your code during development Automate profiling/performance testing Keep historical records of how features perform Consider keeping some (small amount of) profiling code in production
Part 2 High Performance Page Load
Yahoo!'s Exceptional Performance rules Make Fewer HTTP Requests Use a Content Delivery Network Add an Expires Header Gzip Components (including JS!) Put CSS at the Top Move Scripts to the Bottom Avoid CSS Expressions Make JavaScript and CSS External Reduce DNS Lookups Minify JavaScript Avoid Redirects Remove Duplicate Scripts Configure ETags Make Ajax Cacheable See  http://developer.yahoo.com/performance/  for more information. A web page works in 3 (sometimes imbricated) stages: load render run These rules cover  mostly  the first stage.
Asset optimization Minify CSS and JavaScript files: Use the YUI Compressor [  http://developer.yahoo.com/yui/compressor/  ] Stay away from so-called advanced compression schemes - like Packer Combine CSS and JavaScript files: At build time [  http://www.julienlecomte.net/blog/2007/09/16/  ] At run time Optimize image assets: PngCrush [  http://pmt.sourceforge.net/pngcrush/  ] PngOptimizer [  http://psydk.org/PngOptimizer.php  ] etc.
Reduce unminified code size Loading and parsing HTML, CSS and JavaScript code is costly. Be concise and write less code. Make good use of JavaScript libraries. Consider splitting your large JavaScript files into smaller files (bundles) when the parsing and compilation of the script takes an excessive amount of time ( Firefox bug #313967 ) Load code (HTML, CSS and JavaScript) on demand (a.k.a “lazy loading”) See  http://ajaxpatterns.org/On-Demand_Javascript Use the YUI Loader Dojo's package system JSAN Import System
Optimize initial rendering (1/4) Miscellaneous tips... Consider rendering the first view on the server: Be aware of the added page weight You will still need to attach event handlers once the DOM is ready Close Your HTML Tags to Speed Up Parsing: Implicitly closed tags add cost to HTML parsing http://msdn2.microsoft.com/en-us/library/ms533020.aspx#Close_Your_Tags Consider flushing the apache buffer very early on: The download of external CSS files (should be at the top of the page!) may get a head start. May not influence the rendering speed however. Browsers buffer their input before displaying it. Load only essential assets / load assets on a delay or on demand Use the YUI Image Loader
Optimize initial rendering (2/4) Don’t always wait for  onload ... Most DOM operations can be accomplished before the onload event has fired. If you have control over where your code is going to be inserted in the page, write your init code in a  <script>  block located right before the closing  </body>  tag. Otherwise, use the YUI Event utility’s  onDOMReady  method: YAHOO.util.Event.onDOMReady( function  () { // Do something here... // e.g., attach event handlers. });
Optimize initial rendering (3/4) Post-load script loading A well designed site should be fully functional, even without JavaScript enabled. Therefore, you may be able to load scripts on a delay. Doing so benefits the loading of other assets (style sheets, images, etc.) Which makes your site load faster Right before the closing  </body>  tag, insert the following: <script> window.onload =  function  () { var  script = document.createElement( &quot;script&quot; ); script.src = ...; document.body.appendChild(script); }; </script>
Optimize initial rendering (4/4) Conditional preloading Preloading assets (JavaScript, CSS, Images, etc.) has the potential to really enhance the user experience. However, one must be smart about when the preloading takes place. Otherwise, the preloading may actually worsen the user experience... http://www.sitepoint.com/article/web-site-optimization-steps/3 Try it at  http://search.yahoo.com/
Part 3 High Performance JavaScript
Reduce the amount of symbolic look-up: The scope chain (1/2) var  g =  7 ; function  f(a) { var  v =  8 ; x = v + a + g; } f(6); parent Look-up is performed every time a variable is accessed. Variables are resolved backwards from most specific to least specific scope.
Reduce the amount of symbolic look-up: The scope chain (2/2) Therefore, declare (with the  var  keyword) and use variables in the same scope whenever possible, and avoid global variables at all costs. Never use the  with  keyword, as it prevents the compiler from generating code for fast access to local variables (traverse the object prototype chain first, and then up the scope chain, and so on) Cache the result of expensive look-ups in local variables: var  arr = ...; var  globalVar =  0 ; ( function  () { var  i; for (i =  0 ; i < arr.length; i++) { globalVar++; } })(); var  arr = ...; var  globalVar =  0 ; ( function  () { var  i, l, localVar; l = arr.length; localVar = globalVar; for (i =  0 ; i < l; i++) { localVar++; } globalVar = localVar; })(); (faster on all A-grade browsers)
Reduce the amount of symbolic look-up: The prototype chain function  A () {} A.prototype.prop1 = ...; function  B () { this .prop2 = ...; } B.prototype =  new  A(); var  b =  new  B(); B.prototype Accessing members bound to the primary object is about 25% faster than accessing members defined anywhere in the prototype chain. The longer the traversal of the prototype chain, the slower the look-up.
Optimize object instantiation If you need to create many objects, consider adding members to the prototype instead of adding them to each individual object in the object constructor (properties are bound once, to the prototype object) This also reduces memory consumption. However, it slows down the look-up of object members. function  Foo () {...} Foo.prototype.bar =  function  () {...}; function  Foo () { this .bar =  function  () {...}; }
Don’t use  eval ! The string passed to  eval  (and its relatives, the  Function  constructor and the  setTimeout  and  setInterval  functions) needs to be compiled and interpreted. Extremely slow! Never pass a string to the  setTimeout  and  setInterval  functions. Instead, pass an anonymous function like so: setTimeout( function  () { // Code to execute on a timeout },  50 ); Never use  eval  and the  Function  constructor (except in some extremely rare cases, and only in code blocks where performance is not critical)
Optimize string concatenation On Internet Explorer (JScript), concatenating two strings causes a new string to be allocated, and the two original strings to be copied: var  s =  “xxx”  +  “yyy” ; s +=  “zzz” ; Therefore, it is much faster on Internet Explorer to append strings to an array, and then use  Array.join  (don’t use this for simple concatenations!) var  i, s =  “” ; for (i =  0 ; i <  10000 ; i++) { s +=  “x” ; } var  i, s = []; for (i =  0 ; i <  10000 ; i++) { s[i] =  “x” ; } s = s.join( “” ); Other JavaScript engines (WebKit, SpiderMonkey) have been optimized to handle string concatenations by doing a  realloc  +  memcpy  whenever possible. Use the YUI Compressor!
Optimize regular expressions Don’t use the  RegExp  constructor, unless your regular expression is assembled at runtime. Instead, use regular expression literals. Use the  test  method if all you want to do is test for a pattern (the  exec  method carries a small performance penalty) Use non-capturing groups  (?: ... ) Stick to simple patterns. If your regular expression looks like the following, reconsider... if ( /loaded|complete/ .test(document.readyState)) {...} (?:(?:\r\n)?[\t])*(?:(?:(?:[^()<>@,;:\\&quot;.\[\]\000-\031]+(?:(?:(?:\r\n)?[\t])+|\Z|(?=[\[&quot;()<>@,;:\\&quot;.\[\]]))|&quot; (?:[^\&quot;\r\\]|\\.|(?:(?:\r\n)?[\t]))*&quot;(?:(?:\r\n)?[\t])*)(?:\.(?:(?:\r\n)?[\t])*(?:[^()<>@,;:\\&quot;.\[\]\000-\031 ]+(?:(?:(?:\r\n)?[\t])+|\Z|(?=[\[&quot;()<>@,;:\\&quot;.\[\]]))|&quot;(?:[^\&quot;\r\\]|\\.|(?:(?:\r\n)?[\t]))*&quot;(?:(?:\r\n)?[\t]) *))*@(?:(?:\r\n)?[\t])*(?:[^()<>@,;:\\&quot;.\[\]\000-\031]+(?:(?:(?:\r\n)?[\t])+|\Z|(?=[\[&quot;()<>@,;:\\&quot;.\[\]]))|\[ ([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[\t])*)(?:\.(?:(?:\r\n)?[\t])*(?:[^()<>@,;:\\&quot;.\[\]\000-\031]+(?:(?:(?:\r\n)? [\t])+|\Z|(?=[\[&quot;()<>@,;:\\&quot;.\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[\t])*))*|(?:[^()<>@,;:\\&quot;.\[\]\000-\0 31]+(?:(?:(?:\r\n)?[\t])+|\Z|(?=[\[&quot;()<>@,;:\\&quot;.\[\]]))|&quot;(?:[^\&quot;\r\\]|\\.|(?:(?:\r\n)?[\t]))*&quot;(?:(?:\r\n)?[\t ])*)*\<(?:(?:\r\n)?[\t])*(?:@(?:[^()<>@,;:\\&quot;.\[\]\000-\031]+(?:(?:(?:\r\n)?[\t])+|\Z|(?=[\[&quot;()<>@,;:\\&quot;.\[\] ]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[\t])*)(?:\.(?:(?:\r\n)?[\t])*(?:[^()<>@,;:\\&quot;.\[\]\000-\031]+(?:(?:(?: \r\n)?[\t])+|\Z|(?=[\[&quot;()<>@,;:\\&quot;.\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[\t])*))*(?:,@(?:(?:\r\n)?[\t]))
Caching Caching can be justified by: High cost (CPU or network latency) associated with getting a value Value will be read many times And will not change often! Increases memory consumption    tradeoff Memoization: var  fn = ( function  () { var  b =  false , v; return   function  () { if  (!b) { v = ...; b =  true ; } return  v; }; })(); function  fn () { if  (!fn.b) { fn.v = ...; fn.b =  true ; } return  fn.v; } var  fn =  function  () { var  v = ...; return  (fn =  function  () { return  v; })(); }; Module pattern Store value in function object Lazy function definition
How to handle long running JavaScript processes (1/2) During long running JavaScript processes, the entire browser UI is frozen Therefore, to maintain a decent user experience, make sure that JavaScript threads never take more than ~ 300 msec (at most) to complete. You can break long running processes into smaller units of work, and chain them using  setTimeout . You can also process the data on the server. More info at  http://www.julienlecomte.net/blog/2007/10/28 Demo
How to handle long running JavaScript processes (2/2) function  doSomething (callbackFn) { // Initialize a few things here... ( function  () { // Do a little bit of work here... if  ( termination condition ) { // We are done callbackFn(); }  else  { // Process next chunk setTimeout(arguments.callee, 0); } })(); }
Miscellaneous tips (1/2) Primitive operations are often faster than the corresponding function calls: var  a =  1 , b =  2 , c; c = Math.min(a, b); c = a < b ? a : b; If possible, avoid using  try...catch  in performance-critical sections: var  i; for  (i =  0 ; i <  100000 ; i++) { try  { ... }  catch  (e) { ... } } var  i; try  { for  (i =  0 ; i <  100000 ; i++) { ... } }  catch  (e) { ... } myArray.push(value); myArray[myArray.length] = value; myArray[idx++] = value;
Miscellaneous tips (2/2) If possible, avoid  for...in  in performance-critical sections: var  key, value; for  (key in myArray) { value = myArray[key]; ... } var  i, value, length = myArray.length; for  (i =  0 ; i < length; i++) { value = myArray[i]; ... } Branch outside, not inside, whenever the branching condition does not change: function  fn () { if  (...) { ... }  else  { ... } } var  fn; if  (...) { fn =  function  () {...}; }  else  { fn =  function  () {...}; }
Part 4 High Performance Dynamic HTML
Document tree modification Using  innerHTML var  i, j, el, table, tbody, row, cell; el = document.createElement( &quot;div&quot; ); document.body.appendChild(el); table = document.createElement( &quot;table&quot; ); el.appendChild(table); tbody = document.createElement( &quot;tbody&quot; ); table.appendChild(tbody); for  (i =  0 ; i <  1000 ; i++) { row = document.createElement( &quot;tr&quot; ); for  (j =  0 ; j <  5 ; j++) { cell = document.createElement( &quot;td&quot; ); row.appendChild(cell); } tbody.appendChild(row); } var  i, j, el, idx, html; idx =  0 ; html = []; html[idx++] =  &quot;<table>&quot; ; for  (i =  0 ; i <  1000 ; i++) { html[idx++] =  &quot;<tr>&quot; ; for  (j =  0 ; j <  5 ; j++) { html[idx++] =  &quot;<td></td>&quot; ; } html[idx++] =  &quot;</tr>&quot; ; } html[idx++] =  &quot;</table>&quot; ; el = document.createElement( &quot;div&quot; ); document.body.appendChild(el); el.innerHTML = html.join( &quot;&quot; ); ( much  faster on all A-grade browsers) Warning: See  http://www.julienlecomte.net/blog/2007/12/38/
Document tree modification Using  cloneNode var  i, j, el, table, tbody, row, cell; el = document.createElement( &quot;div&quot; ); document.body.appendChild(el); table = document.createElement( &quot;table&quot; ); el.appendChild(table); tbody = document.createElement( &quot;tbody&quot; ); table.appendChild(tbody); for  (i =  0 ; i <  1000 ; i++) { row = document.createElement( &quot;tr&quot; ); for  (j =  0 ; j <  5 ; j++) { cell = document.createElement( &quot;td&quot; ); row.appendChild(cell); } tbody.appendChild(row); } var  i, el, table, tbody, template, row, cell; el = document.createElement( &quot;div&quot; ); document.body.appendChild(el); table = document.createElement( &quot;table&quot; ); el.appendChild(table); tbody = document.createElement( &quot;tbody&quot; ); table.appendChild(tbody); template = document.createElement( &quot;tr&quot; ); for  (i =  0 ; i <  5 ; i++) { cell = document.createElement( &quot;td&quot; ); template.appendChild(cell); } for  (i =  0 ; i <  1000 ; i++) { row = template.cloneNode(true); tbody.appendChild(row); } (faster on all A-grade browsers – sometimes  much  faster) Warning: expando properties/attached event handlers are lost!
Document tree modification Using DocumentFragment A DocumentFragment (DOM Level 1 Core) is a lightweight Document object. It supports only a subset of the regular DOM methods and properties. IE’s implementation of the DocumentFragment interface does not comply with the W3C specification and returns a regular Document object. var  i, j, el, table, tbody, row, cell, docFragment; docFragment = document.createDocumentFragment(); el = document.createElement( &quot;div&quot; ); docFragment.appendChild(el); table = document.createElement( &quot;table&quot; ); el.appendChild(table); tbody = document.createElement( &quot;tbody&quot; ); table.appendChild(tbody); for  (i =  0 ; i <  1000 ; i++) { ... } document.body.appendChild(docFragment);
Limit the number of event handlers (1/2) Attaching an event handler to hundreds of elements is very costly Multiplying event handlers augments the potential for memory leaks Solution: Use  event delegation , a technique that relies on  event bubbling <div id= &quot;container&quot; > <ul> <li id= &quot;li-1&quot; > List Item 1 </li> <li id= &quot;li-2&quot; > List Item 2 </li> <li id= &quot;li-3&quot; > List Item 3 </li> <li id= &quot;li-4&quot; > List Item 4 </li> <li id= &quot;li-5&quot; > List Item 5 </li> ... </ul> </div> div#container ul li#li- x text node
Limit the number of event handlers (2/2) YAHOO.util.Event.addListener( &quot;container&quot; ,  &quot;click&quot; ,  function  (e) { var  el = YAHOO.util.Event.getTarget(e); while (el.id !==  &quot;container&quot; ) { if (el.nodeName.toUpperCase() ===  &quot;LI&quot; ) { // Do something here... break; } else { el = el.parentNode; } } });
Limiting reflows Reflows happen whenever the DOM tree is manipulated. Browsers have optimizations you may take advantage of to minimize reflow: Modifying an invisible element (display:none) does not trigger reflow Modifying an element “off-DOM” does not trigger reflow Batch style changes: Change the value of the style attribute using setAttribute (does not work on Internet Explorer) Example: Change the value of the cssText property of the style object. Example: More maintainable: Change the CSS class name of an element. Example: el.style.cssText =  &quot;display:block;width:auto;height:100px;...&quot; ; YAHOO.util.Dom.replaceClass(el,  &quot;foo&quot; ,  &quot;bar&quot; ); el.setAttribute( &quot;style&quot; ,  &quot;display:block;width:auto;height:100px;...&quot; );
Miscellaneous tips... Consider using the  onmousedown  event instead of the  onclick  event Get a head start by making use of the small delay between the time a user presses the mouse button and the time he/she releases it. “ Downshift your code”: throttle frequent and expensive actions See  http://yuiblog.com/blog/2007/07/09/downshift-your-code/
Part 5 High Performance Layout and CSS
Miscellaneous tips... Use CSS Sprites for Snappy Image Replacement. Avoid using JavaScript for layout. window.onresize is throttled... Use pure CSS instead! Side benefits: improves maintainability, degrades more gracefully, etc. Avoid using Internet Explorer expressions Expressions are constantly evaluated in order to react to environment changes. There are ways to more safely use expressions, but in general, you shouldn’t need/use them. Avoid using Internet Explorer filters (or keep their use to a minimum) Optimize Table Layout Goal: allow the rendering engine to start rendering a table before it has received all the data Use table-layout:fixed Explicitly define a COL element for each column Set the WIDTH attribute on each col Optimize your CSS selectors  [  http://developer.mozilla.org/en/docs/Writing_Efficient_CSS  ]
Part 6 High Performance Ajax
Ajax Best Practices Never resort to using synchronous XMLHttpRequest http://yuiblog.com/blog/2006/04/04/synchronous-v-asynchronous/ Asynchronous programming model slightly more complicated. Need to lock all or part of the UI while the transaction is pending. Programmatically handle network timeouts. Solution: Use the YUI Connection Manager: var  callback = { success:  function  () {  /* Do something */  }, failure:  function  () {  /* Do something */  }, timeout:  5000 }; YAHOO.util.Connect.asyncRequest( &quot;GET&quot; , url, callback);
Improving perceived network latency using the optimistic pattern If the data is validated locally (on the client, using JavaScript) before being sent to the server, the request will be successful in 99.9% of the cases. Therefore, in order to optimize the user experience, we should assume a successful outcome and adopt the following pattern: Update the UI when the request gets sent. Lock the UI/data structures with the finest possible granularity. Let the user know that something is happening. Let the user know why a UI object is locked. Unlock the UI/data structures when the outcome is successful. Handle error cases gracefully.
Miscellaneous tips... Be aware of the maximum number of concurrent HTTP/1.1 connections. Multiplex Ajax requests whenever possible, and if your backend supports it. Piggyback unsollicited notifications in a response to an Ajax request. Favor JSON over XML as your data exchange format Accessing a JSON data structure is easier and cheaper than accessing XML data. JSON has less overhead than XML. Push, don’t poll. Use  COMET  to send real-time notifications to the browser. Consider using local storage to cache data locally, request a diff from the server: Internet Explorer’s userData Flash local storage DOM:Storage (WhatWG persistent storage API, implemented in Firefox 2) Google Gears etc.
Part 7 Performance Tools
Performance Tools YSlow?  [  http://developer.yahoo.com/yslow/  ] Task manager IE Leak Detector a.k.a Drip  [  http://www.outofhanwell.com/ieleak/  ] Stopwatch profiling AjaxView  [  http://research.microsoft.com/projects/ajaxview/  ] JsLex  [  http://rockstarapps.com/pmwiki/pmwiki.php?n=JsLex.JsLex  ] YUI profiler [  http://developer.yahoo.com/yui/profiler/  ] Venkman or Firebug Profiler  [  http://www.getfirebug.com/  ]

High Performance Ajax Applications

  • 1.
    High Performance AjaxApplications Julien Lecomte http://www.julienlecomte.net/blogfiles/performance/ajax-perf.ppt http://www.slideshare.net/julien.lecomte/high-performance-ajax-applications
  • 2.
    Part 1 DevelopingFor High Performance
  • 3.
    Planning and designingfor high performance Plan for performance from day 1 Work closely with designers and product managers Understand design rationale Explain the tradeoffs between design and performance Offer alternatives and show what is possible (prototype) Challenge yourself to implement challenging designs (don't just say no) Help simplify the design and interaction if needed (compromise)
  • 4.
    Engineering high performance:A few basic rules Less is more Don’t do anything unnecessary. Don’t do anything until it becomes absolutely necessary. Break the rules Make compromises and break best practices, but only as a last resort! Work on improving perceived performance Users can deal with some reasonable amount of slowness if: They are informed appropriately that an operation is pending. The user interface remains reactive at all time. Cheat whenever you can by first updating the UI and then do the work. Need to “lock” all or part of the user interface.
  • 5.
    Measuring performance Testperformance using a setup similar to your users’ environment Profile your code during development Automate profiling/performance testing Keep historical records of how features perform Consider keeping some (small amount of) profiling code in production
  • 6.
    Part 2 HighPerformance Page Load
  • 7.
    Yahoo!'s Exceptional Performancerules Make Fewer HTTP Requests Use a Content Delivery Network Add an Expires Header Gzip Components (including JS!) Put CSS at the Top Move Scripts to the Bottom Avoid CSS Expressions Make JavaScript and CSS External Reduce DNS Lookups Minify JavaScript Avoid Redirects Remove Duplicate Scripts Configure ETags Make Ajax Cacheable See http://developer.yahoo.com/performance/ for more information. A web page works in 3 (sometimes imbricated) stages: load render run These rules cover mostly the first stage.
  • 8.
    Asset optimization MinifyCSS and JavaScript files: Use the YUI Compressor [ http://developer.yahoo.com/yui/compressor/ ] Stay away from so-called advanced compression schemes - like Packer Combine CSS and JavaScript files: At build time [ http://www.julienlecomte.net/blog/2007/09/16/ ] At run time Optimize image assets: PngCrush [ http://pmt.sourceforge.net/pngcrush/ ] PngOptimizer [ http://psydk.org/PngOptimizer.php ] etc.
  • 9.
    Reduce unminified codesize Loading and parsing HTML, CSS and JavaScript code is costly. Be concise and write less code. Make good use of JavaScript libraries. Consider splitting your large JavaScript files into smaller files (bundles) when the parsing and compilation of the script takes an excessive amount of time ( Firefox bug #313967 ) Load code (HTML, CSS and JavaScript) on demand (a.k.a “lazy loading”) See http://ajaxpatterns.org/On-Demand_Javascript Use the YUI Loader Dojo's package system JSAN Import System
  • 10.
    Optimize initial rendering(1/4) Miscellaneous tips... Consider rendering the first view on the server: Be aware of the added page weight You will still need to attach event handlers once the DOM is ready Close Your HTML Tags to Speed Up Parsing: Implicitly closed tags add cost to HTML parsing http://msdn2.microsoft.com/en-us/library/ms533020.aspx#Close_Your_Tags Consider flushing the apache buffer very early on: The download of external CSS files (should be at the top of the page!) may get a head start. May not influence the rendering speed however. Browsers buffer their input before displaying it. Load only essential assets / load assets on a delay or on demand Use the YUI Image Loader
  • 11.
    Optimize initial rendering(2/4) Don’t always wait for onload ... Most DOM operations can be accomplished before the onload event has fired. If you have control over where your code is going to be inserted in the page, write your init code in a <script> block located right before the closing </body> tag. Otherwise, use the YUI Event utility’s onDOMReady method: YAHOO.util.Event.onDOMReady( function () { // Do something here... // e.g., attach event handlers. });
  • 12.
    Optimize initial rendering(3/4) Post-load script loading A well designed site should be fully functional, even without JavaScript enabled. Therefore, you may be able to load scripts on a delay. Doing so benefits the loading of other assets (style sheets, images, etc.) Which makes your site load faster Right before the closing </body> tag, insert the following: <script> window.onload = function () { var script = document.createElement( &quot;script&quot; ); script.src = ...; document.body.appendChild(script); }; </script>
  • 13.
    Optimize initial rendering(4/4) Conditional preloading Preloading assets (JavaScript, CSS, Images, etc.) has the potential to really enhance the user experience. However, one must be smart about when the preloading takes place. Otherwise, the preloading may actually worsen the user experience... http://www.sitepoint.com/article/web-site-optimization-steps/3 Try it at http://search.yahoo.com/
  • 14.
    Part 3 HighPerformance JavaScript
  • 15.
    Reduce the amountof symbolic look-up: The scope chain (1/2) var g = 7 ; function f(a) { var v = 8 ; x = v + a + g; } f(6); parent Look-up is performed every time a variable is accessed. Variables are resolved backwards from most specific to least specific scope.
  • 16.
    Reduce the amountof symbolic look-up: The scope chain (2/2) Therefore, declare (with the var keyword) and use variables in the same scope whenever possible, and avoid global variables at all costs. Never use the with keyword, as it prevents the compiler from generating code for fast access to local variables (traverse the object prototype chain first, and then up the scope chain, and so on) Cache the result of expensive look-ups in local variables: var arr = ...; var globalVar = 0 ; ( function () { var i; for (i = 0 ; i < arr.length; i++) { globalVar++; } })(); var arr = ...; var globalVar = 0 ; ( function () { var i, l, localVar; l = arr.length; localVar = globalVar; for (i = 0 ; i < l; i++) { localVar++; } globalVar = localVar; })(); (faster on all A-grade browsers)
  • 17.
    Reduce the amountof symbolic look-up: The prototype chain function A () {} A.prototype.prop1 = ...; function B () { this .prop2 = ...; } B.prototype = new A(); var b = new B(); B.prototype Accessing members bound to the primary object is about 25% faster than accessing members defined anywhere in the prototype chain. The longer the traversal of the prototype chain, the slower the look-up.
  • 18.
    Optimize object instantiationIf you need to create many objects, consider adding members to the prototype instead of adding them to each individual object in the object constructor (properties are bound once, to the prototype object) This also reduces memory consumption. However, it slows down the look-up of object members. function Foo () {...} Foo.prototype.bar = function () {...}; function Foo () { this .bar = function () {...}; }
  • 19.
    Don’t use eval ! The string passed to eval (and its relatives, the Function constructor and the setTimeout and setInterval functions) needs to be compiled and interpreted. Extremely slow! Never pass a string to the setTimeout and setInterval functions. Instead, pass an anonymous function like so: setTimeout( function () { // Code to execute on a timeout }, 50 ); Never use eval and the Function constructor (except in some extremely rare cases, and only in code blocks where performance is not critical)
  • 20.
    Optimize string concatenationOn Internet Explorer (JScript), concatenating two strings causes a new string to be allocated, and the two original strings to be copied: var s = “xxx” + “yyy” ; s += “zzz” ; Therefore, it is much faster on Internet Explorer to append strings to an array, and then use Array.join (don’t use this for simple concatenations!) var i, s = “” ; for (i = 0 ; i < 10000 ; i++) { s += “x” ; } var i, s = []; for (i = 0 ; i < 10000 ; i++) { s[i] = “x” ; } s = s.join( “” ); Other JavaScript engines (WebKit, SpiderMonkey) have been optimized to handle string concatenations by doing a realloc + memcpy whenever possible. Use the YUI Compressor!
  • 21.
    Optimize regular expressionsDon’t use the RegExp constructor, unless your regular expression is assembled at runtime. Instead, use regular expression literals. Use the test method if all you want to do is test for a pattern (the exec method carries a small performance penalty) Use non-capturing groups (?: ... ) Stick to simple patterns. If your regular expression looks like the following, reconsider... if ( /loaded|complete/ .test(document.readyState)) {...} (?:(?:\r\n)?[\t])*(?:(?:(?:[^()<>@,;:\\&quot;.\[\]\000-\031]+(?:(?:(?:\r\n)?[\t])+|\Z|(?=[\[&quot;()<>@,;:\\&quot;.\[\]]))|&quot; (?:[^\&quot;\r\\]|\\.|(?:(?:\r\n)?[\t]))*&quot;(?:(?:\r\n)?[\t])*)(?:\.(?:(?:\r\n)?[\t])*(?:[^()<>@,;:\\&quot;.\[\]\000-\031 ]+(?:(?:(?:\r\n)?[\t])+|\Z|(?=[\[&quot;()<>@,;:\\&quot;.\[\]]))|&quot;(?:[^\&quot;\r\\]|\\.|(?:(?:\r\n)?[\t]))*&quot;(?:(?:\r\n)?[\t]) *))*@(?:(?:\r\n)?[\t])*(?:[^()<>@,;:\\&quot;.\[\]\000-\031]+(?:(?:(?:\r\n)?[\t])+|\Z|(?=[\[&quot;()<>@,;:\\&quot;.\[\]]))|\[ ([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[\t])*)(?:\.(?:(?:\r\n)?[\t])*(?:[^()<>@,;:\\&quot;.\[\]\000-\031]+(?:(?:(?:\r\n)? [\t])+|\Z|(?=[\[&quot;()<>@,;:\\&quot;.\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[\t])*))*|(?:[^()<>@,;:\\&quot;.\[\]\000-\0 31]+(?:(?:(?:\r\n)?[\t])+|\Z|(?=[\[&quot;()<>@,;:\\&quot;.\[\]]))|&quot;(?:[^\&quot;\r\\]|\\.|(?:(?:\r\n)?[\t]))*&quot;(?:(?:\r\n)?[\t ])*)*\<(?:(?:\r\n)?[\t])*(?:@(?:[^()<>@,;:\\&quot;.\[\]\000-\031]+(?:(?:(?:\r\n)?[\t])+|\Z|(?=[\[&quot;()<>@,;:\\&quot;.\[\] ]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[\t])*)(?:\.(?:(?:\r\n)?[\t])*(?:[^()<>@,;:\\&quot;.\[\]\000-\031]+(?:(?:(?: \r\n)?[\t])+|\Z|(?=[\[&quot;()<>@,;:\\&quot;.\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[\t])*))*(?:,@(?:(?:\r\n)?[\t]))
  • 22.
    Caching Caching canbe justified by: High cost (CPU or network latency) associated with getting a value Value will be read many times And will not change often! Increases memory consumption  tradeoff Memoization: var fn = ( function () { var b = false , v; return function () { if (!b) { v = ...; b = true ; } return v; }; })(); function fn () { if (!fn.b) { fn.v = ...; fn.b = true ; } return fn.v; } var fn = function () { var v = ...; return (fn = function () { return v; })(); }; Module pattern Store value in function object Lazy function definition
  • 23.
    How to handlelong running JavaScript processes (1/2) During long running JavaScript processes, the entire browser UI is frozen Therefore, to maintain a decent user experience, make sure that JavaScript threads never take more than ~ 300 msec (at most) to complete. You can break long running processes into smaller units of work, and chain them using setTimeout . You can also process the data on the server. More info at http://www.julienlecomte.net/blog/2007/10/28 Demo
  • 24.
    How to handlelong running JavaScript processes (2/2) function doSomething (callbackFn) { // Initialize a few things here... ( function () { // Do a little bit of work here... if ( termination condition ) { // We are done callbackFn(); } else { // Process next chunk setTimeout(arguments.callee, 0); } })(); }
  • 25.
    Miscellaneous tips (1/2)Primitive operations are often faster than the corresponding function calls: var a = 1 , b = 2 , c; c = Math.min(a, b); c = a < b ? a : b; If possible, avoid using try...catch in performance-critical sections: var i; for (i = 0 ; i < 100000 ; i++) { try { ... } catch (e) { ... } } var i; try { for (i = 0 ; i < 100000 ; i++) { ... } } catch (e) { ... } myArray.push(value); myArray[myArray.length] = value; myArray[idx++] = value;
  • 26.
    Miscellaneous tips (2/2)If possible, avoid for...in in performance-critical sections: var key, value; for (key in myArray) { value = myArray[key]; ... } var i, value, length = myArray.length; for (i = 0 ; i < length; i++) { value = myArray[i]; ... } Branch outside, not inside, whenever the branching condition does not change: function fn () { if (...) { ... } else { ... } } var fn; if (...) { fn = function () {...}; } else { fn = function () {...}; }
  • 27.
    Part 4 HighPerformance Dynamic HTML
  • 28.
    Document tree modificationUsing innerHTML var i, j, el, table, tbody, row, cell; el = document.createElement( &quot;div&quot; ); document.body.appendChild(el); table = document.createElement( &quot;table&quot; ); el.appendChild(table); tbody = document.createElement( &quot;tbody&quot; ); table.appendChild(tbody); for (i = 0 ; i < 1000 ; i++) { row = document.createElement( &quot;tr&quot; ); for (j = 0 ; j < 5 ; j++) { cell = document.createElement( &quot;td&quot; ); row.appendChild(cell); } tbody.appendChild(row); } var i, j, el, idx, html; idx = 0 ; html = []; html[idx++] = &quot;<table>&quot; ; for (i = 0 ; i < 1000 ; i++) { html[idx++] = &quot;<tr>&quot; ; for (j = 0 ; j < 5 ; j++) { html[idx++] = &quot;<td></td>&quot; ; } html[idx++] = &quot;</tr>&quot; ; } html[idx++] = &quot;</table>&quot; ; el = document.createElement( &quot;div&quot; ); document.body.appendChild(el); el.innerHTML = html.join( &quot;&quot; ); ( much faster on all A-grade browsers) Warning: See http://www.julienlecomte.net/blog/2007/12/38/
  • 29.
    Document tree modificationUsing cloneNode var i, j, el, table, tbody, row, cell; el = document.createElement( &quot;div&quot; ); document.body.appendChild(el); table = document.createElement( &quot;table&quot; ); el.appendChild(table); tbody = document.createElement( &quot;tbody&quot; ); table.appendChild(tbody); for (i = 0 ; i < 1000 ; i++) { row = document.createElement( &quot;tr&quot; ); for (j = 0 ; j < 5 ; j++) { cell = document.createElement( &quot;td&quot; ); row.appendChild(cell); } tbody.appendChild(row); } var i, el, table, tbody, template, row, cell; el = document.createElement( &quot;div&quot; ); document.body.appendChild(el); table = document.createElement( &quot;table&quot; ); el.appendChild(table); tbody = document.createElement( &quot;tbody&quot; ); table.appendChild(tbody); template = document.createElement( &quot;tr&quot; ); for (i = 0 ; i < 5 ; i++) { cell = document.createElement( &quot;td&quot; ); template.appendChild(cell); } for (i = 0 ; i < 1000 ; i++) { row = template.cloneNode(true); tbody.appendChild(row); } (faster on all A-grade browsers – sometimes much faster) Warning: expando properties/attached event handlers are lost!
  • 30.
    Document tree modificationUsing DocumentFragment A DocumentFragment (DOM Level 1 Core) is a lightweight Document object. It supports only a subset of the regular DOM methods and properties. IE’s implementation of the DocumentFragment interface does not comply with the W3C specification and returns a regular Document object. var i, j, el, table, tbody, row, cell, docFragment; docFragment = document.createDocumentFragment(); el = document.createElement( &quot;div&quot; ); docFragment.appendChild(el); table = document.createElement( &quot;table&quot; ); el.appendChild(table); tbody = document.createElement( &quot;tbody&quot; ); table.appendChild(tbody); for (i = 0 ; i < 1000 ; i++) { ... } document.body.appendChild(docFragment);
  • 31.
    Limit the numberof event handlers (1/2) Attaching an event handler to hundreds of elements is very costly Multiplying event handlers augments the potential for memory leaks Solution: Use event delegation , a technique that relies on event bubbling <div id= &quot;container&quot; > <ul> <li id= &quot;li-1&quot; > List Item 1 </li> <li id= &quot;li-2&quot; > List Item 2 </li> <li id= &quot;li-3&quot; > List Item 3 </li> <li id= &quot;li-4&quot; > List Item 4 </li> <li id= &quot;li-5&quot; > List Item 5 </li> ... </ul> </div> div#container ul li#li- x text node
  • 32.
    Limit the numberof event handlers (2/2) YAHOO.util.Event.addListener( &quot;container&quot; , &quot;click&quot; , function (e) { var el = YAHOO.util.Event.getTarget(e); while (el.id !== &quot;container&quot; ) { if (el.nodeName.toUpperCase() === &quot;LI&quot; ) { // Do something here... break; } else { el = el.parentNode; } } });
  • 33.
    Limiting reflows Reflowshappen whenever the DOM tree is manipulated. Browsers have optimizations you may take advantage of to minimize reflow: Modifying an invisible element (display:none) does not trigger reflow Modifying an element “off-DOM” does not trigger reflow Batch style changes: Change the value of the style attribute using setAttribute (does not work on Internet Explorer) Example: Change the value of the cssText property of the style object. Example: More maintainable: Change the CSS class name of an element. Example: el.style.cssText = &quot;display:block;width:auto;height:100px;...&quot; ; YAHOO.util.Dom.replaceClass(el, &quot;foo&quot; , &quot;bar&quot; ); el.setAttribute( &quot;style&quot; , &quot;display:block;width:auto;height:100px;...&quot; );
  • 34.
    Miscellaneous tips... Considerusing the onmousedown event instead of the onclick event Get a head start by making use of the small delay between the time a user presses the mouse button and the time he/she releases it. “ Downshift your code”: throttle frequent and expensive actions See http://yuiblog.com/blog/2007/07/09/downshift-your-code/
  • 35.
    Part 5 HighPerformance Layout and CSS
  • 36.
    Miscellaneous tips... UseCSS Sprites for Snappy Image Replacement. Avoid using JavaScript for layout. window.onresize is throttled... Use pure CSS instead! Side benefits: improves maintainability, degrades more gracefully, etc. Avoid using Internet Explorer expressions Expressions are constantly evaluated in order to react to environment changes. There are ways to more safely use expressions, but in general, you shouldn’t need/use them. Avoid using Internet Explorer filters (or keep their use to a minimum) Optimize Table Layout Goal: allow the rendering engine to start rendering a table before it has received all the data Use table-layout:fixed Explicitly define a COL element for each column Set the WIDTH attribute on each col Optimize your CSS selectors [ http://developer.mozilla.org/en/docs/Writing_Efficient_CSS ]
  • 37.
    Part 6 HighPerformance Ajax
  • 38.
    Ajax Best PracticesNever resort to using synchronous XMLHttpRequest http://yuiblog.com/blog/2006/04/04/synchronous-v-asynchronous/ Asynchronous programming model slightly more complicated. Need to lock all or part of the UI while the transaction is pending. Programmatically handle network timeouts. Solution: Use the YUI Connection Manager: var callback = { success: function () { /* Do something */ }, failure: function () { /* Do something */ }, timeout: 5000 }; YAHOO.util.Connect.asyncRequest( &quot;GET&quot; , url, callback);
  • 39.
    Improving perceived networklatency using the optimistic pattern If the data is validated locally (on the client, using JavaScript) before being sent to the server, the request will be successful in 99.9% of the cases. Therefore, in order to optimize the user experience, we should assume a successful outcome and adopt the following pattern: Update the UI when the request gets sent. Lock the UI/data structures with the finest possible granularity. Let the user know that something is happening. Let the user know why a UI object is locked. Unlock the UI/data structures when the outcome is successful. Handle error cases gracefully.
  • 40.
    Miscellaneous tips... Beaware of the maximum number of concurrent HTTP/1.1 connections. Multiplex Ajax requests whenever possible, and if your backend supports it. Piggyback unsollicited notifications in a response to an Ajax request. Favor JSON over XML as your data exchange format Accessing a JSON data structure is easier and cheaper than accessing XML data. JSON has less overhead than XML. Push, don’t poll. Use COMET to send real-time notifications to the browser. Consider using local storage to cache data locally, request a diff from the server: Internet Explorer’s userData Flash local storage DOM:Storage (WhatWG persistent storage API, implemented in Firefox 2) Google Gears etc.
  • 41.
  • 42.
    Performance Tools YSlow? [ http://developer.yahoo.com/yslow/ ] Task manager IE Leak Detector a.k.a Drip [ http://www.outofhanwell.com/ieleak/ ] Stopwatch profiling AjaxView [ http://research.microsoft.com/projects/ajaxview/ ] JsLex [ http://rockstarapps.com/pmwiki/pmwiki.php?n=JsLex.JsLex ] YUI profiler [ http://developer.yahoo.com/yui/profiler/ ] Venkman or Firebug Profiler [ http://www.getfirebug.com/ ]