High Performance JavaScript Nicholas C. Zakas | Amazon DevCon | April 27, 2011
Who's this guy? Presentation Contributor, Architect Creator of YUI Test Author Lead Author Contributor Lead Author
@slicknet (Complaints: @robertduffy)
Does JavaScript performance matter?
After all, all browsers now have optimizing JavaScript engines Tracemonkey/ V8 Squirrelfish Chakra Karakan JaegarMonkey (all) (4+) (9+) (10.5+) (3.5+)
So our scripts are getting really, really fast
Old computers ran slow applications Small amounts of CPU power and memory
New computers are generally faster but slow applications still exist More CPU + more memory = less disciplined application development
Right???
Oh yeah, one more thing
http://jeftek.com/1942/motorola-xoom-sunspider-results/
It's still possible to write slow JavaScript on the new, faster JavaScript engines
JavaScript performance directly affects user experience
"Know the enemy and know yourself; in a hundred battles you will never be in peril." -Sun Tzu, The Art of War
The UI Thread The brains of the operation
The browser UI thread is responsible for both UI updates and JavaScript execution Only one can happen at a time
Jobs for UI updates and JavaScript execution are added to a UI queue Each job must wait in line for its turn to execute
<button id="btn" style="font-size: 30px; padding: 0.5em 1em">Click Me</button> <script type="text/javascript"> window.onload = function(){ document.getElementById("btn").onclick = function(){ //do something }; }; </script>
Before Click UI Thread time UI Queue
When Clicked UI Thread time UI Queue UI Update onclick UI Update
When Clicked UI Thread UI Update time UI Queue onclick Draw down state UI Update
When Clicked UI Thread UI Update onclick time UI Queue UI Update
When Clicked UI Thread UI Update onclick UI Update time UI Queue Draw up state
No UI updates while JavaScript is executing
JavaScript May Cause UI Update <button id="btn" style="font-size: 30px; padding: 0.5em 1em">Click Me</button> <script type="text/javascript"> window.onload = function(){ document.getElementById("btn").onclick = function(){ var div = document.createElement(“div”); div.className = “tip”; div.innerHTML = “You clicked me!”; document.body.appendChild(div); }; }; </script>
A UI update must use the latest info available
Long-running JavaScript = Unresponsive UI
Responsive UI UI Thread UI Update JavaScript UI Update time
Unresponsive UI UI Thread UI Update JavaScript UI Update time
The longer JavaScript runs, the worse the user experience
The runaway script timer prevents JavaScript from running for too long Each browser imposes its own limit (except Opera)
Internet Explorer
Firefox
Safari
Chrome
Runaway Script Timer Limits • Internet Explorer: 5 million statements • Firefox: 10 seconds • Safari: 5 seconds • Chrome: Unknown, hooks into normal crash control mechanism • Opera: none
Does JIT compiling help?
Interpreted JavaScript UI Thread Interpret time
JITed JavaScript (1st Run) UI Thread Compile Execute time
JITed JavaScript (After 1st Run) UI Thread Execute time
How Long Is Too Long? “0.1 second [100ms] is about the limit for having the user feel that the system is reacting instantaneously, meaning that no special feedback is necessary except to display the result.” - Jakob Nielsen
Translation: No single JavaScript job should execute for more than 100ms to ensure a responsive UI
Recommendation: Limit JavaScript execution to no more than 50ms measured on IE6 :)
Doing so makes your application awesome
Loadtime Techniques Don't let JavaScript interfere with page load performance
During page load, JavaScript takes more time on the UI thread
<!doctype html> <html> <head> <title>Example</title> </head> <body> <p>Hello world!</p> <script src="foo.js"></script> <p>See ya!</p> </body> </html>
Result UI Thread UI Update JavaScript UI Update time
Result UI Thread Hello world! foo.js See ya! time
Result UI Thread Hello world! Download Parse Run See ya! time The UI thread needs to wait for the script to download, parse, and run before continuing
Result UI Thread Hello world! Download Parse Run See ya! Variable Constant Download time takes the longest and is variable
Translation: The page doesn't render while JavaScript is downloading, parsing, or executing during page load
<!doctype html> <html> <head> <title>Example</title> </head> <body> <script src="foo.js"></script> <p>Hello world!</p> <script src="bar.js"></script> <p>See ya!</p> <script src="baz.js"></script> <p>Uh oh!</p> </body> </html>
Result UI Thread JavaScript UI Update JavaScript UI Update JavaScript time The more scripts to download in between UI updates, the longer the page takes to render
Technique #1: Put scripts at the bottom
<!doctype html> <html> <head> <title>Example</title> </head> <body> <p>Hello world!</p> <p>See ya!</p> <script src="foo.js"></script> </body> </html>
Put Scripts at Bottom UI Thread UI Update UI Update JavaScript JavaScript JavaScript time Even if there are multiple scripts, the page renders quickly
Technique #2: Combine JavaScript files
<!doctype html> <html> <head> <title>Example</title> </head> <body> <p>Hello world!</p> <p>See ya!</p> <script src="foo.js"></script> <script src="bar.js"></script> <script src="baz.js"></script> </body> </html>
UI Thread UI Update JavaScript JavaScript JavaScript time Each script has overhead of downloading
UI Thread UI Update JavaScript time Combining all of the files limits the network overhead and gets scripts onto the page faster
<!doctype html> <html> <head> <title>Example</title> </head> <body> <p>Hello world!</p> <p>See ya!</p> <script src="foo-and-bar-and-baz.js"></script> </body> </html>
Technique #3: Load scripts dynamically
Basic Technique var script = document.createElement("script"), body; script.type = "text/javascript"; script.src = "foo.js"; body.insertBefore(script, body.firstChild); Dynamically loaded scripts are non-blocking
Downloads no longer block the UI thread
<!doctype html> <html> <head> <title>Example</title> </head> <body> <p>Hello world!</p> <script src="foo.js"></script> <p>See ya!</p> </body> </html>
Using HTML <script> UI Thread Hello world! Download Parse Run See ya! time
<!doctype html> <html> <head> <title>Example</title> </head> <body> <p>Hello world!</p> <script> var script = document.createElement("script"), body = document.body; script.type = "text/javascript"; script.src = "foo.js"; body.insertBefore(script, body.firstChild); </script> <p>See ya!</p><!-- more content --> </body> </html>
Using Dynamic Scripts UI Thread Hello world! See ya! Run UI Update time Download Parse Only code execution happens on the UI thread, which means less blocking of UI updates
function loadScript(url, callback){ var script = document.createElement("script"), body = document.body; script.type = "text/javascript"; if (script.readyState){ //IE <= 8 script.onreadystatechange = function(){ if (script.readyState == "loaded" || script.readyState == "complete"){ script.onreadystatechange = null; callback(); } }; } else { //Others script.onload = function(){ callback(); }; } script.src = url; body.insertBefore(script, body.firstChild); }
Usage loadScript("foo.js", function(){ alert("Loaded!"); });
Timing Note: Script execution begins immediately after download and parse – timing of execution is not guaranteed
Using Dynamic Scripts UI Thread Hello world! Run See ya! UI Update time Download Parse Depending on time to download and script size, execution may happen before next UI update
Technique #4: Defer scripts
<!doctype html> <html> <head> <title>Example</title> </head> <body> <p>Hello world!</p> <script defer src="foo.js"></script> <p>See ya!</p> <!-- even more markup --> </body> </html>
Support for <script defer> 3.5 7.0 5.0 4.0 ?
Deferred scripts begin to download immediately, but don't execute until all UI updates complete
Using <script defer> UI Thread Hello world! See ya! More UI More UI Run time Download Parse Similar to dynamic script nodes, but with a guarantee that execution will happen last
Timing Note: Although scripts always execute after UI updates complete, the order of multiple <script defer> scripts is not guaranteed across browsers
Technique #5: Asynchronous scripts
<!doctype html> <html> <head> <title>Example</title> </head> <body> <p>Hello world!</p> <script async src="foo.js"></script> <p>See ya!</p> <!-- even more markup --> </body> </html>
Support for <script async> 3.6 7.0 5.0 ? ?
Asynchronous scripts behave a lot like dynamic scripts
Using <script async> UI Thread Hello world! See ya! Run UI Update time Download Parse Download begins immediately and execution is slotted in at first available spot
Note: Order of execution is explicitly not preserved for asynchronous scripts
Runtime Techniques Ways to ensure JavaScript doesn't run away
function processArray(items, process, callback){ for (var i=0,len=items.length; i < len; i++){ process(items[i]); } callback(); }
Technique #1: Timers
//create a new timer and delay by 500ms setTimeout(function(){ //code to execute here }, 500) setTimeout() schedules a function to be added to the UI queue after a delay
function timedProcessArray(items, process, callback){ //create a clone of the original var todo = items.concat(); setTimeout(function(){ var start = +new Date(); do { process(todo.shift()); } while (todo.length > 0 && (+new Date() - start < 50)); if (todo.length > 0){ setTimeout(arguments.callee, 25); } else { callback(items); } }, 25); }
When Clicked UI Thread time UI Queue UI Update onclick UI Update
When Clicked UI Thread UI Update time UI Queue onclick UI Update
When Clicked UI Thread UI Update onclick time UI Queue UI Update
When Clicked UI Thread UI Update onclick UI Update time UI Queue
After 25ms UI Thread UI Update onclick UI Update time UI Queue JavaScript
After 25ms UI Thread UI Update onclick UI Update JavaScript time UI Queue
After Another 25ms UI Thread UI Update onclick UI Update JavaScript time UI Queue JavaScript
After Another 25ms UI Thread UI Update onclick UI Update JavaScript JavaScript time UI Queue
Technique #2: Web Workers
Web Workers ● Asynchronous JavaScript execution ● Execution happens outside of UI thread ● Not on the UI thread = no UI delays ● Data-driven API ● Data is serialized when sending data into or out of Worker ● No access to DOM, BOM ● Completely separate execution environment
//in page var worker = new Worker("process.js"); worker.onmessage = function(event){ useData(event.data); }; worker.postMessage(values); //in process.js self.onmessage = function(event){ var items = event.data; for (var i=0,len=items.length; i < len; i++){ process(items[i]); } self.postMessage(items); };
When Clicked UI Thread time UI Queue UI Update onclick UI Update
When Clicked UI Thread UI Update time UI Queue onclick UI Update
When Clicked UI Thread UI Update onclick time UI Queue UI Update
When Clicked UI Thread UI Update onclick time Worker Thread UI Queue UI Update
When Clicked UI Thread UI Update onclick UI Update time Worker Thread UI Queue JavaScript
Worker Thread Complete UI Thread UI Update onclick UI Update time UI Queue onmessage
Worker Thread Complete UI Thread UI Update onclick UI Update onmessage time UI Queue
Support for Web Workers 3.5 4.0 4.0 ? 10.6
Repaint and Reflow Hidden performance costs of common operations
Long UI updates = Unresponsive UI
Unresponsive UI UI Thread UI Update JavaScript UI Update time
JavaScript can cause long UI updates by triggering repaint and reflow
A repaint occurs when a visual change doesn't require recalculation of layout Changes to visibility, colors (text/background), background images, etc.
Repaint <button id="btn" style="font-size: 30px; padding: 0.5em 1em">Click Me</button> <script type="text/javascript"> window.onload = function(){ document.getElementById("btn").onclick = function(){ this.style.color = "#ff0"; }; }; </script> Repaint!
A reflow occurs when a visual change requires a change in layout Initial page load ▪ browser resize ▪ DOM structure change ▪ layout style change layout information retrieved
Reflow <button id="btn" style="font-size: 30px; padding: 0.5em 1em">Click Me</button> <script type="text/javascript"> window.onload = function(){ document.getElementById("btn").onclick = function(){ var div = document.createElement(“div”); div.className = “tip”; div.innerHTML = “You clicked me!”; document.body.appendChild(div); }; }; </script> Reflow!
Repaints and reflows are queued up as JavaScript executes and then executed in order
Reflow var list = document.getElementsByClassName("items")[0], i, item; for (i=0; i < 10; i++){ item = document.createElement("li"); item.innerHTML = "Item #" + i; list.appendChild(item); } Reflow x 10!
Limiting repaints/reflows improves overall performance
Technique #1 Perform DOM manipulations off-document
Off-Document Operations • Fast because there's no repaint/reflow • Techniques: – Remove element from the document, make changes, insert back into document – Set element's display to “none”, make changes, set display back to default – Build up DOM changes on a DocumentFragment then apply all at once
DocumentFragment • A document-like object • Not visually represented • Considered to be owned by the document from which it was created • When passed to appendChild(), appends all of its children rather than itself
DocumentFragment var list = document.getElementsByClassName("items")[0], fragment = document.createDocumentFragment(), i, item; for (i=0; i < 10; i++){ item = document.createElement("li"); item.innerHTML = "Item #" + i; fragment.appendChild(item); } list.appendChild(fragment); 1 Reflow
Technique #2 Group Style Changes
Repaint! Reflow! element.style.color = "red"; element.style.height = "100px"; Reflow! element.style.fontSize = "25px"; element.style.backgroundColor = "white"; Repaint!
Grouping Style Changes .active { color: red; height: 100px; fontSize: 25px; background-color: white; } element.className = "active"; Reflow!
Grouping Style Changes var item = document.getElementById("myItem"); item.style.cssText = "color:red;height:100px;" + "font-size:25px;background-color: white"); Reflow!
Technique #3 Avoid Accidental Reflow
Accidental Reflow element.width = "100px"; var width = element.offsetWidth; Reflow!
What to do? • Minimize access to layout information – offsetTop, offsetLeft, offsetWidth, offsetHeight – scrollTop, scrollLeft, scrollWidth, scrollHeight – clientTop, clientLeft, clientWidth, clientHeight – Most computed styles • If a value is used more than once, store in local variable
Does hardware acceleration help?
Traditional Rendering UI Thread Compositing Drawing time
Hardware Acceleration UI Thread Prep Wait time Rendering info Signal complete Compositing Drawing GPU
Recap
The browser UI thread is responsible for both UI updates and JavaScript execution Only one can happen at a time
Responsive UI UI Thread UI Update JavaScript UI Update time
Unresponsive UI UI Thread UI Update JavaScript UI Update time
Unresponsive UI UI Thread UI Update JavaScript UI Update time
Avoid Slow Loading JavaScript • Put scripts at the bottom • Concatenate scripts into as few files as possible • Choose the right way to load your scripts – Dynamically created scripts – Deferred scripts – Asynchronous scripts
Avoid Slow JavaScript • Don't allow JavaScript to execute for more than 50ms • Break up long JavaScript processes using: – Timers – Web Workers
Avoid Long UI Updates • Be careful of repaint and reflow • Perform complex DOM operations off- document – Remove elements and re-add them – Use DocumentFragment objects • Group style changes together • Avoid accidental reflow
Don't be horrible
Do be awesome
The End
Etcetera • My blog: www.nczonline.net • Twitter: @slicknet • These Slides: http://slideshare.net/nzakas/
Creative Commons Images Used • http://www.flickr.com/photos/lrargerich/3115367361/ • http://www.flickr.com/photos/hippie/2406411610/ • http://www.flickr.com/photos/55733754@N00/3325000738/ • http://www.flickr.com/photos/eurleif/255241547/ • http://www.flickr.com/photos/off_the_wall/3444915939/ • http://www.flickr.com/photos/wwarby/3296379139/ • http://www.flickr.com/photos/derekgavey/4358797365/ • http://www.flickr.com/photos/mulad/286641998/ • http://www.flickr.com/photos/torley/2361164281/ • http://www.flickr.com/photos/ottoman42/455242/ • http://www.flickr.com/photos/goincase/3843348908/

High Performance JavaScript (Amazon DevCon 2011)

  • 1.
    High Performance JavaScript Nicholas C. Zakas | Amazon DevCon | April 27, 2011
  • 2.
    Who's this guy? Presentation Contributor, Architect Creator of YUI Test Author Lead Author Contributor Lead Author
  • 4.
  • 6.
  • 7.
    After all, allbrowsers now have optimizing JavaScript engines Tracemonkey/ V8 Squirrelfish Chakra Karakan JaegarMonkey (all) (4+) (9+) (10.5+) (3.5+)
  • 8.
    So our scriptsare getting really, really fast
  • 9.
    Old computers ranslow applications Small amounts of CPU power and memory
  • 10.
    New computers aregenerally faster but slow applications still exist More CPU + more memory = less disciplined application development
  • 11.
  • 14.
    Oh yeah, onemore thing
  • 16.
  • 17.
    It's still possibleto write slow JavaScript on the new, faster JavaScript engines
  • 18.
    JavaScript performance directly affects user experience
  • 19.
    "Know the enemyand know yourself; in a hundred battles you will never be in peril." -Sun Tzu, The Art of War
  • 20.
    The UI Thread Thebrains of the operation
  • 21.
    The browser UIthread is responsible for both UI updates and JavaScript execution Only one can happen at a time
  • 22.
    Jobs for UIupdates and JavaScript execution are added to a UI queue Each job must wait in line for its turn to execute
  • 23.
    <button id="btn" style="font-size:30px; padding: 0.5em 1em">Click Me</button> <script type="text/javascript"> window.onload = function(){ document.getElementById("btn").onclick = function(){ //do something }; }; </script>
  • 24.
  • 25.
    When Clicked UI Thread time UI Queue UI Update onclick UI Update
  • 26.
    When Clicked UI Thread UI Update time UI Queue onclick Draw down state UI Update
  • 27.
    When Clicked UI Thread UI Update onclick time UI Queue UI Update
  • 28.
    When Clicked UI Thread UI Update onclick UI Update time UI Queue Draw up state
  • 29.
    No UI updateswhile JavaScript is executing
  • 30.
    JavaScript May CauseUI Update <button id="btn" style="font-size: 30px; padding: 0.5em 1em">Click Me</button> <script type="text/javascript"> window.onload = function(){ document.getElementById("btn").onclick = function(){ var div = document.createElement(“div”); div.className = “tip”; div.innerHTML = “You clicked me!”; document.body.appendChild(div); }; }; </script>
  • 31.
    A UI updatemust use the latest info available
  • 32.
    Long-running JavaScript = Unresponsive UI
  • 33.
    Responsive UI UI Thread UI Update JavaScript UI Update time
  • 34.
    Unresponsive UI UI Thread UI Update JavaScript UI Update time
  • 35.
    The longer JavaScriptruns, the worse the user experience
  • 36.
    The runaway scripttimer prevents JavaScript from running for too long Each browser imposes its own limit (except Opera)
  • 37.
  • 38.
  • 39.
  • 40.
  • 42.
    Runaway Script TimerLimits • Internet Explorer: 5 million statements • Firefox: 10 seconds • Safari: 5 seconds • Chrome: Unknown, hooks into normal crash control mechanism • Opera: none
  • 43.
  • 44.
  • 45.
    JITed JavaScript (1stRun) UI Thread Compile Execute time
  • 46.
    JITed JavaScript (After1st Run) UI Thread Execute time
  • 47.
    How Long IsToo Long? “0.1 second [100ms] is about the limit for having the user feel that the system is reacting instantaneously, meaning that no special feedback is necessary except to display the result.” - Jakob Nielsen
  • 48.
    Translation: No singleJavaScript job should execute for more than 100ms to ensure a responsive UI
  • 49.
    Recommendation: Limit JavaScript executionto no more than 50ms measured on IE6 :)
  • 50.
    Doing so makesyour application awesome
  • 51.
    Loadtime Techniques Don't letJavaScript interfere with page load performance
  • 52.
    During page load,JavaScript takes more time on the UI thread
  • 53.
    <!doctype html> <html> <head> <title>Example</title> </head> <body> <p>Hello world!</p> <script src="foo.js"></script> <p>See ya!</p> </body> </html>
  • 54.
    Result UI Thread UIUpdate JavaScript UI Update time
  • 55.
    Result UI Thread Helloworld! foo.js See ya! time
  • 56.
    Result UI Thread Helloworld! Download Parse Run See ya! time The UI thread needs to wait for the script to download, parse, and run before continuing
  • 57.
    Result UI Thread Helloworld! Download Parse Run See ya! Variable Constant Download time takes the longest and is variable
  • 58.
    Translation: The page doesn't render while JavaScript is downloading, parsing, or executing during page load
  • 59.
    <!doctype html> <html> <head> <title>Example</title> </head> <body> <script src="foo.js"></script> <p>Hello world!</p> <script src="bar.js"></script> <p>See ya!</p> <script src="baz.js"></script> <p>Uh oh!</p> </body> </html>
  • 60.
    Result UI Thread JavaScript UI Update JavaScript UI Update JavaScript time The more scripts to download in between UI updates, the longer the page takes to render
  • 61.
    Technique #1: Putscripts at the bottom
  • 63.
    <!doctype html> <html> <head> <title>Example</title> </head> <body> <p>Hello world!</p> <p>See ya!</p> <script src="foo.js"></script> </body> </html>
  • 64.
    Put Scripts atBottom UI Thread UI Update UI Update JavaScript JavaScript JavaScript time Even if there are multiple scripts, the page renders quickly
  • 65.
    Technique #2: Combine JavaScript files
  • 66.
    <!doctype html> <html> <head> <title>Example</title> </head> <body> <p>Hello world!</p> <p>See ya!</p> <script src="foo.js"></script> <script src="bar.js"></script> <script src="baz.js"></script> </body> </html>
  • 67.
    UI Thread UI Update JavaScript JavaScript JavaScript time Each script has overhead of downloading
  • 68.
    UI Thread UI Update JavaScript time Combining all of the files limits the network overhead and gets scripts onto the page faster
  • 69.
    <!doctype html> <html> <head> <title>Example</title> </head> <body> <p>Hello world!</p> <p>See ya!</p> <script src="foo-and-bar-and-baz.js"></script> </body> </html>
  • 70.
    Technique #3: Loadscripts dynamically
  • 71.
    Basic Technique var script= document.createElement("script"), body; script.type = "text/javascript"; script.src = "foo.js"; body.insertBefore(script, body.firstChild); Dynamically loaded scripts are non-blocking
  • 72.
    Downloads no longerblock the UI thread
  • 73.
    <!doctype html> <html> <head> <title>Example</title> </head> <body> <p>Hello world!</p> <script src="foo.js"></script> <p>See ya!</p> </body> </html>
  • 74.
    Using HTML <script> UIThread Hello world! Download Parse Run See ya! time
  • 75.
    <!doctype html> <html> <head> <title>Example</title> </head> <body> <p>Hello world!</p> <script> var script = document.createElement("script"), body = document.body; script.type = "text/javascript"; script.src = "foo.js"; body.insertBefore(script, body.firstChild); </script> <p>See ya!</p><!-- more content --> </body> </html>
  • 76.
    Using Dynamic Scripts UIThread Hello world! See ya! Run UI Update time Download Parse Only code execution happens on the UI thread, which means less blocking of UI updates
  • 77.
    function loadScript(url, callback){ var script = document.createElement("script"), body = document.body; script.type = "text/javascript"; if (script.readyState){ //IE <= 8 script.onreadystatechange = function(){ if (script.readyState == "loaded" || script.readyState == "complete"){ script.onreadystatechange = null; callback(); } }; } else { //Others script.onload = function(){ callback(); }; } script.src = url; body.insertBefore(script, body.firstChild); }
  • 78.
  • 79.
    Timing Note: Scriptexecution begins immediately after download and parse – timing of execution is not guaranteed
  • 80.
    Using Dynamic Scripts UIThread Hello world! Run See ya! UI Update time Download Parse Depending on time to download and script size, execution may happen before next UI update
  • 81.
  • 82.
    <!doctype html> <html> <head> <title>Example</title> </head> <body> <p>Hello world!</p> <script defer src="foo.js"></script> <p>See ya!</p> <!-- even more markup --> </body> </html>
  • 83.
    Support for <scriptdefer> 3.5 7.0 5.0 4.0 ?
  • 84.
    Deferred scripts beginto download immediately, but don't execute until all UI updates complete
  • 85.
    Using <script defer> UIThread Hello world! See ya! More UI More UI Run time Download Parse Similar to dynamic script nodes, but with a guarantee that execution will happen last
  • 86.
    Timing Note: Although scriptsalways execute after UI updates complete, the order of multiple <script defer> scripts is not guaranteed across browsers
  • 87.
  • 88.
    <!doctype html> <html> <head> <title>Example</title> </head> <body> <p>Hello world!</p> <script async src="foo.js"></script> <p>See ya!</p> <!-- even more markup --> </body> </html>
  • 89.
    Support for <scriptasync> 3.6 7.0 5.0 ? ?
  • 90.
    Asynchronous scripts behavea lot like dynamic scripts
  • 91.
    Using <script async> UIThread Hello world! See ya! Run UI Update time Download Parse Download begins immediately and execution is slotted in at first available spot
  • 92.
    Note: Order of executionis explicitly not preserved for asynchronous scripts
  • 93.
    Runtime Techniques Ways toensure JavaScript doesn't run away
  • 94.
    function processArray(items, process,callback){ for (var i=0,len=items.length; i < len; i++){ process(items[i]); } callback(); }
  • 95.
  • 96.
    //create a newtimer and delay by 500ms setTimeout(function(){ //code to execute here }, 500) setTimeout() schedules a function to be added to the UI queue after a delay
  • 97.
    function timedProcessArray(items, process,callback){ //create a clone of the original var todo = items.concat(); setTimeout(function(){ var start = +new Date(); do { process(todo.shift()); } while (todo.length > 0 && (+new Date() - start < 50)); if (todo.length > 0){ setTimeout(arguments.callee, 25); } else { callback(items); } }, 25); }
  • 98.
    When Clicked UI Thread time UI Queue UI Update onclick UI Update
  • 99.
    When Clicked UI Thread UI Update time UI Queue onclick UI Update
  • 100.
    When Clicked UI Thread UI Update onclick time UI Queue UI Update
  • 101.
    When Clicked UI Thread UI Update onclick UI Update time UI Queue
  • 102.
    After 25ms UI Thread UI Update onclick UI Update time UI Queue JavaScript
  • 103.
    After 25ms UI Thread UI Update onclick UI Update JavaScript time UI Queue
  • 104.
    After Another 25ms UIThread UI Update onclick UI Update JavaScript time UI Queue JavaScript
  • 105.
    After Another 25ms UIThread UI Update onclick UI Update JavaScript JavaScript time UI Queue
  • 106.
  • 108.
    Web Workers ● Asynchronous JavaScript execution ● Execution happens outside of UI thread ● Not on the UI thread = no UI delays ● Data-driven API ● Data is serialized when sending data into or out of Worker ● No access to DOM, BOM ● Completely separate execution environment
  • 109.
    //in page var worker= new Worker("process.js"); worker.onmessage = function(event){ useData(event.data); }; worker.postMessage(values); //in process.js self.onmessage = function(event){ var items = event.data; for (var i=0,len=items.length; i < len; i++){ process(items[i]); } self.postMessage(items); };
  • 110.
    When Clicked UI Thread time UI Queue UI Update onclick UI Update
  • 111.
    When Clicked UI Thread UI Update time UI Queue onclick UI Update
  • 112.
    When Clicked UI Thread UI Update onclick time UI Queue UI Update
  • 113.
    When Clicked UI Thread UI Update onclick time Worker Thread UI Queue UI Update
  • 114.
    When Clicked UI Thread UI Update onclick UI Update time Worker Thread UI Queue JavaScript
  • 115.
    Worker Thread Complete UIThread UI Update onclick UI Update time UI Queue onmessage
  • 116.
    Worker Thread Complete UIThread UI Update onclick UI Update onmessage time UI Queue
  • 117.
    Support for WebWorkers 3.5 4.0 4.0 ? 10.6
  • 118.
    Repaint and Reflow Hiddenperformance costs of common operations
  • 119.
    Long UI updates = Unresponsive UI
  • 120.
    Unresponsive UI UI Thread UI Update JavaScript UI Update time
  • 121.
    JavaScript can cause longUI updates by triggering repaint and reflow
  • 122.
    A repaint occurswhen a visual change doesn't require recalculation of layout Changes to visibility, colors (text/background), background images, etc.
  • 123.
    Repaint <button id="btn" style="font-size:30px; padding: 0.5em 1em">Click Me</button> <script type="text/javascript"> window.onload = function(){ document.getElementById("btn").onclick = function(){ this.style.color = "#ff0"; }; }; </script> Repaint!
  • 124.
    A reflow occurswhen a visual change requires a change in layout Initial page load ▪ browser resize ▪ DOM structure change ▪ layout style change layout information retrieved
  • 125.
    Reflow <button id="btn" style="font-size:30px; padding: 0.5em 1em">Click Me</button> <script type="text/javascript"> window.onload = function(){ document.getElementById("btn").onclick = function(){ var div = document.createElement(“div”); div.className = “tip”; div.innerHTML = “You clicked me!”; document.body.appendChild(div); }; }; </script> Reflow!
  • 126.
    Repaints and reflowsare queued up as JavaScript executes and then executed in order
  • 127.
    Reflow var list =document.getElementsByClassName("items")[0], i, item; for (i=0; i < 10; i++){ item = document.createElement("li"); item.innerHTML = "Item #" + i; list.appendChild(item); } Reflow x 10!
  • 128.
  • 129.
    Technique #1 Perform DOMmanipulations off-document
  • 130.
    Off-Document Operations • Fastbecause there's no repaint/reflow • Techniques: – Remove element from the document, make changes, insert back into document – Set element's display to “none”, make changes, set display back to default – Build up DOM changes on a DocumentFragment then apply all at once
  • 131.
    DocumentFragment • A document-likeobject • Not visually represented • Considered to be owned by the document from which it was created • When passed to appendChild(), appends all of its children rather than itself
  • 132.
    DocumentFragment var list =document.getElementsByClassName("items")[0], fragment = document.createDocumentFragment(), i, item; for (i=0; i < 10; i++){ item = document.createElement("li"); item.innerHTML = "Item #" + i; fragment.appendChild(item); } list.appendChild(fragment); 1 Reflow
  • 133.
  • 134.
    Repaint! Reflow! element.style.color = "red"; element.style.height = "100px"; Reflow! element.style.fontSize = "25px"; element.style.backgroundColor = "white"; Repaint!
  • 135.
    Grouping Style Changes .active{ color: red; height: 100px; fontSize: 25px; background-color: white; } element.className = "active"; Reflow!
  • 136.
    Grouping Style Changes varitem = document.getElementById("myItem"); item.style.cssText = "color:red;height:100px;" + "font-size:25px;background-color: white"); Reflow!
  • 137.
  • 138.
    Accidental Reflow element.width ="100px"; var width = element.offsetWidth; Reflow!
  • 139.
    What to do? •Minimize access to layout information – offsetTop, offsetLeft, offsetWidth, offsetHeight – scrollTop, scrollLeft, scrollWidth, scrollHeight – clientTop, clientLeft, clientWidth, clientHeight – Most computed styles • If a value is used more than once, store in local variable
  • 140.
  • 141.
    Traditional Rendering UI Thread Compositing Drawing time
  • 142.
    Hardware Acceleration UI Thread Prep Wait time Rendering info Signal complete Compositing Drawing GPU
  • 143.
  • 144.
    The browser UIthread is responsible for both UI updates and JavaScript execution Only one can happen at a time
  • 145.
    Responsive UI UI Thread UI Update JavaScript UI Update time
  • 146.
    Unresponsive UI UI Thread UI Update JavaScript UI Update time
  • 147.
    Unresponsive UI UI Thread UI Update JavaScript UI Update time
  • 148.
    Avoid Slow LoadingJavaScript • Put scripts at the bottom • Concatenate scripts into as few files as possible • Choose the right way to load your scripts – Dynamically created scripts – Deferred scripts – Asynchronous scripts
  • 149.
    Avoid Slow JavaScript •Don't allow JavaScript to execute for more than 50ms • Break up long JavaScript processes using: – Timers – Web Workers
  • 150.
    Avoid Long UIUpdates • Be careful of repaint and reflow • Perform complex DOM operations off- document – Remove elements and re-add them – Use DocumentFragment objects • Group style changes together • Avoid accidental reflow
  • 151.
  • 152.
  • 153.
  • 154.
    Etcetera • My blog: www.nczonline.net • Twitter: @slicknet • These Slides: http://slideshare.net/nzakas/
  • 155.
    Creative Commons ImagesUsed • http://www.flickr.com/photos/lrargerich/3115367361/ • http://www.flickr.com/photos/hippie/2406411610/ • http://www.flickr.com/photos/55733754@N00/3325000738/ • http://www.flickr.com/photos/eurleif/255241547/ • http://www.flickr.com/photos/off_the_wall/3444915939/ • http://www.flickr.com/photos/wwarby/3296379139/ • http://www.flickr.com/photos/derekgavey/4358797365/ • http://www.flickr.com/photos/mulad/286641998/ • http://www.flickr.com/photos/torley/2361164281/ • http://www.flickr.com/photos/ottoman42/455242/ • http://www.flickr.com/photos/goincase/3843348908/