SlideShare a Scribd company logo
1 of 116
Making Ajax Sexy
A Look at Sequential Ajax Patterns
View source
http://github.com/furf/Sexy
Go to website…
http://sexyjs.com
NSFW?
?SFW
But really, “assholes?”
WARNING
This presentation contains a very bawdy joke.
WARNING
If very bawdy jokes are the sort of thing that
offend you or make you uncomfortable in the
    trousers, you might want to leave now.
David Furfero
@furf   UI Engineer, MLB.com
SERIOUSLY
  Go, now.
YOU  DATA
Ajax makes killer web apps possible.
Sequential Ajax makes Ajax killer.
Sequential Ajax makes Ajax killer.
Sexy.js makes Sequential Ajax…Sexy!
Sexy.js makes Sequential Ajax…Sexy!
Sequential Ajax (Sajax)
An Ajax pattern for handling multiple
asynchronous requests while maintaining
proper callback execution order.
Goals of Sequential Ajax (Sajax)

• Facilitate combination and manipulation of data from
  multiple sources

• Speed loading of data, scripts, stylesheets using parallel
  asynchronous HTTP requests

• Speed execution of callbacks by allowing them to fire as
  soon as all previous data is available

• Fun
Three Flavors of Sajax

• Serial request / serial response
• Parallel request / random response
• Parallel request / serial response
In a perfect world…
In a world, where time waits for
no man…
or his data…
Sequential Ajax Anti-Patterns
Serial Request / Serial Response
// Request an article
$.getJSON('/article/1.json', function (article) {

  // Request the related comments
  $.getJSON('/article/1/comments.json', function (comments) {

   // Join the data
   article.comments = comments;

    // Render the article and comments
    render(article);
  });
});
Serial Request /
Serial Response

• Pros

  • Relatively readable

• Cons

  • Slower

  • Nested calls don’t
    scale
Parallel Request / Random Response
var i = 0, n = 2, article, comments;

$.getJSON('/article/1.json', function (data) {
  article = data;
  if (++i == n) {
    handleComplete();
  }
});

$.getJSON('/article/1/comments.json', function (data) {
  comments = data;
  if (++i == n) {
    handleComplete();
  }
});

function handleComplete () {
  article.comments = comments;
  render(article);
}
Parallel Request /
Random Response

• Pros

  • Faster

• Cons

  • Heavier

  • Uglier

  • Really doesn’t scale
Do those patterns look familiar?
Meet Sexy.js…
Sexy is a jQuery plugin
A lightweight JavaScript library which provides parallel
request/serial response Sequential Ajax (Sajax) functionality
and a sleek facade to jQuery’s native Ajax methods.
Sexy is a standalone library
A lightweight JavaScript library which provides parallel
request/serial response Sequential Ajax (Sajax) functionality
and a sleek facade to jQuery’s native Ajax methods
JavaScript.
Sexy is a standalone library
A lightweight JavaScript library which provides parallel
request/serial response Sequential Ajax (Sajax) functionality
and a sleek facade to jQuery’s native Ajax methods
JavaScript (using jQuery’s native Ajax methods).
Sexy is classy
new Sexy() creates a Sexy instance
Sexy is classy
var s = new Sexy();

s.json(articleUrl, callback)
 .json(commentsUrl, callback);
Sexy is the new Sexy
Sexy() also creates a Sexy instance
Sexy is the new Sexy
Sexy()
  .json(articleUrl, callback)
  .json(commentsUrl, callback);
Naked Sexy
/**
 * Constructs a new Sexy instance
 */
function Sexy (cfg) {

    /**
      * Allow instantiation without new keyword
      */
    if (!(this instanceof Sexy)) {
       return new Sexy(cfg);
    }

    this.cfgs = [];
    this.setup(cfg);
}
Sexy is full of surprises
Sexy.fn() also creates a Sexy instance!
Sexy is full of surprises
Sexy
  .json(articleUrl, callback)
  .json(commentsUrl, callback);
Naked Sexy
/**
  * Add sexier static methods
  */
function addStaticMethod (method) {
   Sexy[method] = function () {
      return Sexy.prototype[method].apply(new Sexy(), arguments);
   };
}

for (i in Sexy.prototype) {
  addStaticMethod(i);
}
I WASN’T KIDDING!
There is a bawdy joke coming up VERY soon!
I WASN’T KIDDING!
   If you are pregnant, have a serious heart
condition, or giggle uncontrollably at the word
 “titillate”, you may want to hurry to Track A.
Expressive, Chainable API
Sexy is Your Syntactic Sugar Mama

An expressive method for each supported data type:
html          (url, callback)
json          (url, callback)
jsonp         (url, callback)
script / js   (url, callback)
text          (url, callback)
xml           (url, callback)
style / css   (url, callback)
Sexy is Patient

Requests can be deferred using an optional 2nd argument.
html          (url, defer, callback)
json          (url, defer, callback)
jsonp         (url, defer, callback)
script / js   (url, defer, callback)
text          (url, defer, callback)
xml           (url, defer, callback)
style / css   (url, defer, callback)
Sexy is Flexible

Each method can also take a jQuery settings object.
html          (settings)
json          (settings)
jsonp         (settings)
script / js   (settings)
text          (settings)
xml           (settings)
style / css   (settings)
callback (data, previous, next, status)

Each callback receives four arguments:
data          response data of the current request
previous      return value of the previous callback
next          Ajax settings of the next request
status        result status of the current request
Sexy Sequential Ajax Patterns
Sexy
Sexy
  .json({
     url: '/article/1.json',
     success: function (article) {
       return article;
     }
  })
  .json({
     url: '/article/1/comments.json',
     success: function (comments, article) {
       article.comments = comments;
       render(article);
     }
  });
Passing Data with Callbacks
Sexy
  .json({
     url: '/article/1.json',
     success: function (article, previous, next, status) {
       return article;
     }
  })
  .json({
     url: '/article/1/comments.json',
     success: function (comments, article, next, status) {
       article.comments = comments;
       render(article);
     }
  });
Sexy-er
Sexy
  .json('/article/1.json', function (article) {
     return article;
  })
  .json('/article/1/comments.json', function (comments, article) {
     article.comments = comments;
     render(article);
  });
Passing Data with Callbacks
Sexy
  .json('/article/1.json', function (article) {
     return article;
  })
  .json('/article/1/comments.json', function (comments, article) {
     article.comments = comments;
     render(article);
  });
Sexy-est
Sexy
  .json('/article/1.json')
  .json('/article/1/comments.json', function (comments, article) {
     article.comments = comments;
     render(article);
  });
Passing Data with Implicit Callbacks
Sexy
  .json('/article/1.json')
  .json('/article/1/comments.json', function (comments, article) {
     article.comments = comments;
     render(article);
  });
Passing Data with Implicit Callbacks
Sexy
  .json('/article/1.json')
  .js('jquery.js')
  .json('/article/1/comments.json', function (comments, article) {
     article.comments = comments;
     render(article);
  });
Slow it Down
Sexy
  // Authenticate user
  .json({
     type: 'POST',
     url: 'login',
     data: { username: 'furf', password: '2s3xy4u' }
  })
  // GET article
  .json('article/1', true, function (article, previous, nextCfg) {
     article.author = 'furf';
     nextCfg.data = article;
  })
  // POST article
  .json({
     defer: true,
     type: 'POST',
     url: 'article/1'
  })
  // De-authenticate user
  .json('logout', true);
Manage <script> Dependencies
Sexy
  .js('jquery.js')
  .js('jquery-ui.js')
  .js('jquery-ui-i18n.js')
  .js('jquery.template.js')
  .js('mlb.js')
  .js('mlb.tracking.js')
  .js('mlb.datagrid.js')
  .js('mlb.stats.js')
  .js('mlb.stats.util.js')
  .js('mlb.stats.widget.js')
  .js('mlb.stats.sortableplayers.js', function () {
     mlb.stats.SortablePlayers.init();
  });
A Sexy Cinderella Story
Old and Busted
var i = 0, n = 2, article, comments;

$.getJSON('/article/1.json', function (data) {
  article = data;
  if (++i == n) {
    handleComplete();
  }
});

$.getJSON('/article/1/comments.json', function (data) {
  comments = data;
  if (++i == n) {
    handleComplete();
  }
});

function handleComplete () {
  article.comments = comments;
  render(article);
}
Your Royal Hotness
Sexy
  .json('/article/1.json')
  .json('/article/1/comments.json', function (comments, article) {
     article.comments = comments;
     render(article);
  });
The 4th Stage of Grief
          ACCEPTANCE
  There’s no way out now. Strap yourself in.
Accept your fate. The bawdy joke is on its way.
Additional Methods
setup (settings)

• Configure subsequent requests of a Sexy instance

• Settings can be unset with setup()
setup (settings)
Sexy
  .setup({
     beforeSend: showLoadingIndicator
     complete:   hideLoadingIndicator
  })
  .json('article.json', diplayArticle)

 .setup()
 .json('comments.json', displayComments);
Sexy (settings)
Sexy({
     beforeSend: showLoadingIndicator
     complete:   hideLoadingIndicator
  })
  .json('article.json', diplayArticle)

 .setup()
 .json('comments.json', displayComments);
bundle (url1, url2, ..., callback)

• Client-side JavaScript packaging

• Multiple file, single <script> injection

• Sequence is preserved
Why not Sexy.package?
JavaScript was too reserved.
*groan*
:)
I told you it was bawdy!
bundle (url1, url2, ..., callback)

• Client-side JavaScript packaging

• Multiple file, single <script> injection

• Sequence is preserved
bundle (url1, url2, ..., callback)
Sexy.bundle(
   'jquery.js',
   'jquery-ui.js',
   'jquery-ui-i18n.js',
   'jquery.template.js',
   'mlb.js',
   'mlb.tracking.js',
   'mlb.datagrid.js',
   'mlb.stats.js',
   'mlb.stats.util.js',
   'mlb.stats.widget.js',
   'mlb.stats.app.sortableplayers.js',
   function () {
     mlb.stats.app.SortablePlayers.init();
   }
);
The Kama Sutra of Sexy.js
var s = Sexy
  .js('jquery.min.js')
  .js('jquery.template.min.js')
  .css('friends.css')
  .text('friend.tpl')
  .json('friends.json', function (data, tpl) {

    // Send a new request for each friend (executed simultaneously)
    $.each(data.friends, function (i, friend) {
      s.json(friend.username + '.json', function (friend, friends) {

       // Push onto the previously returned array
       friends.push(friend);

        // Until the last friend, return the augmented array to the next callback
        if (i < data.friends.length - 1) {
          return friends;

       // When we reach the last friend, render the data and insert into the DOM
       } else {
         $.template(tpl, friends).appendTo('#friends');
       }
     });
   });

   // Instantiate the friends array
   return [];
 });
Known Issues

• Unable to detect error events for remote requests

  • jsonp (remoteUrl)

  • script (remoteUrl)

  • style (remoteUrl)

• style (remoteUrl) fires the success callback even if the
  request was unsuccessful (ie. 404)
Parallel Request / Serial Response

• Pros

  • Faster

  • Cleaner

  • Prettier

• Cons

  • Sexy.js adds
    1.4Kb–4.6Kb to
    your JavaScript
    library
Parallel Request / Serial Response

• Pros

  • Faster

  • Cleaner

  • Prettier

• Cons

  • Sexy.js adds
    1.4Kb–4.6Kb to
    your JavaScript
    library
Can’t get enough?

website      http://sexyjs.com
slideshow    http://www.slideshare.net/furf
download     http://github.com/furf/Sexy
twitter      @sexyjs or @furf
email        furf@furf.com
@thanks!
jsconf, voodootikigod, jquery, jeresig,
jaubourg, dimak25, subchild, brandonaaron,
yoni, jsconfeu, getify, maraksquires,
attricetta, and you!

More Related Content

What's hot

Hidden Treasures in Project Wonder
Hidden Treasures in Project WonderHidden Treasures in Project Wonder
Hidden Treasures in Project Wonder
WO Community
 
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2KZepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Thomas Fuchs
 
Scala ActiveRecord
Scala ActiveRecordScala ActiveRecord
Scala ActiveRecord
scalaconfjp
 

What's hot (20)

Unit Testing in SilverStripe
Unit Testing in SilverStripeUnit Testing in SilverStripe
Unit Testing in SilverStripe
 
SilverStripe CMS JavaScript Refactoring
SilverStripe CMS JavaScript RefactoringSilverStripe CMS JavaScript Refactoring
SilverStripe CMS JavaScript Refactoring
 
Promise pattern
Promise patternPromise pattern
Promise pattern
 
Getting Started with React
Getting Started with ReactGetting Started with React
Getting Started with React
 
Javascript Testing with Jasmine 101
Javascript Testing with Jasmine 101Javascript Testing with Jasmine 101
Javascript Testing with Jasmine 101
 
How to write easy-to-test JavaScript
How to write easy-to-test JavaScriptHow to write easy-to-test JavaScript
How to write easy-to-test JavaScript
 
Art of Javascript
Art of JavascriptArt of Javascript
Art of Javascript
 
JavaScript Promises
JavaScript PromisesJavaScript Promises
JavaScript Promises
 
JavaScript Promises
JavaScript PromisesJavaScript Promises
JavaScript Promises
 
Javascript Promises/Q Library
Javascript Promises/Q LibraryJavascript Promises/Q Library
Javascript Promises/Q Library
 
Hidden Treasures in Project Wonder
Hidden Treasures in Project WonderHidden Treasures in Project Wonder
Hidden Treasures in Project Wonder
 
React.js & Om: A hands-on walkthrough of better ways to build web UIs
React.js & Om: A hands-on walkthrough of better ways to build web UIsReact.js & Om: A hands-on walkthrough of better ways to build web UIs
React.js & Om: A hands-on walkthrough of better ways to build web UIs
 
Discover React
Discover ReactDiscover React
Discover React
 
JavaScript Promise
JavaScript PromiseJavaScript Promise
JavaScript Promise
 
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2KZepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
 
Jasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishyJasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishy
 
Promises, Promises
Promises, PromisesPromises, Promises
Promises, Promises
 
Scala ActiveRecord
Scala ActiveRecordScala ActiveRecord
Scala ActiveRecord
 
CDI: How do I ?
CDI: How do I ?CDI: How do I ?
CDI: How do I ?
 
Beyond Profilers: Tracing Node.js Transactions
Beyond Profilers: Tracing Node.js TransactionsBeyond Profilers: Tracing Node.js Transactions
Beyond Profilers: Tracing Node.js Transactions
 

Viewers also liked

Awla nov 2 presentation final
Awla nov 2 presentation finalAwla nov 2 presentation final
Awla nov 2 presentation final
Dee Schiavelli
 
Einddocument bo c
Einddocument bo cEinddocument bo c
Einddocument bo c
Remcovh
 
biechthokje_battle
biechthokje_battlebiechthokje_battle
biechthokje_battle
Remcovh
 
Battle6 v3
Battle6 v3Battle6 v3
Battle6 v3
Remcovh
 
Straatjutten
StraatjuttenStraatjutten
Straatjutten
Remcovh
 
Alvaro practise2
Alvaro practise2Alvaro practise2
Alvaro practise2
guest64a99a
 

Viewers also liked (8)

Awla nov 2 presentation final
Awla nov 2 presentation finalAwla nov 2 presentation final
Awla nov 2 presentation final
 
Einddocument bo c
Einddocument bo cEinddocument bo c
Einddocument bo c
 
biechthokje_battle
biechthokje_battlebiechthokje_battle
biechthokje_battle
 
Battle6 v3
Battle6 v3Battle6 v3
Battle6 v3
 
Straatjutten
StraatjuttenStraatjutten
Straatjutten
 
Alvaro practise2
Alvaro practise2Alvaro practise2
Alvaro practise2
 
Hype vs. Reality: The AI Explainer
Hype vs. Reality: The AI ExplainerHype vs. Reality: The AI Explainer
Hype vs. Reality: The AI Explainer
 
Study: The Future of VR, AR and Self-Driving Cars
Study: The Future of VR, AR and Self-Driving CarsStudy: The Future of VR, AR and Self-Driving Cars
Study: The Future of VR, AR and Self-Driving Cars
 

Similar to Making Ajax Sexy, JSConf 2010

Build web application by express
Build web application by expressBuild web application by express
Build web application by express
Shawn Meng
 
Ajax tutorial
Ajax tutorialAjax tutorial
Ajax tutorial
Kat Roque
 
Matthew Eernisse, NodeJs, .toster {webdev}
Matthew Eernisse, NodeJs, .toster {webdev}Matthew Eernisse, NodeJs, .toster {webdev}
Matthew Eernisse, NodeJs, .toster {webdev}
.toster
 
international PHP2011_Bastian Feder_jQuery's Secrets
international PHP2011_Bastian Feder_jQuery's Secretsinternational PHP2011_Bastian Feder_jQuery's Secrets
international PHP2011_Bastian Feder_jQuery's Secrets
smueller_sandsmedia
 
Avinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPressAvinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPress
wpnepal
 
Building Smart Async Functions For Mobile
Building Smart Async Functions For MobileBuilding Smart Async Functions For Mobile
Building Smart Async Functions For Mobile
Glan Thomas
 

Similar to Making Ajax Sexy, JSConf 2010 (20)

Sexy.js: A Sequential Ajax (Sajax) library for jQuery
Sexy.js: A Sequential Ajax (Sajax) library for jQuerySexy.js: A Sequential Ajax (Sajax) library for jQuery
Sexy.js: A Sequential Ajax (Sajax) library for jQuery
 
Adding a modern twist to legacy web applications
Adding a modern twist to legacy web applicationsAdding a modern twist to legacy web applications
Adding a modern twist to legacy web applications
 
What's new in jQuery 1.5
What's new in jQuery 1.5What's new in jQuery 1.5
What's new in jQuery 1.5
 
Build web application by express
Build web application by expressBuild web application by express
Build web application by express
 
Backbone.js
Backbone.jsBackbone.js
Backbone.js
 
Avoiding Callback Hell with Async.js
Avoiding Callback Hell with Async.jsAvoiding Callback Hell with Async.js
Avoiding Callback Hell with Async.js
 
Intro to Sail.js
Intro to Sail.jsIntro to Sail.js
Intro to Sail.js
 
Jasmine BDD for Javascript
Jasmine BDD for JavascriptJasmine BDD for Javascript
Jasmine BDD for Javascript
 
Ajax Under The Hood
Ajax Under The HoodAjax Under The Hood
Ajax Under The Hood
 
Understanding backbonejs
Understanding backbonejsUnderstanding backbonejs
Understanding backbonejs
 
Why Our Code Smells
Why Our Code SmellsWhy Our Code Smells
Why Our Code Smells
 
RingoJS
RingoJSRingoJS
RingoJS
 
Ajax tutorial
Ajax tutorialAjax tutorial
Ajax tutorial
 
JavaScript Fundamentals with Angular and Lodash
JavaScript Fundamentals with Angular and LodashJavaScript Fundamentals with Angular and Lodash
JavaScript Fundamentals with Angular and Lodash
 
Matthew Eernisse, NodeJs, .toster {webdev}
Matthew Eernisse, NodeJs, .toster {webdev}Matthew Eernisse, NodeJs, .toster {webdev}
Matthew Eernisse, NodeJs, .toster {webdev}
 
international PHP2011_Bastian Feder_jQuery's Secrets
international PHP2011_Bastian Feder_jQuery's Secretsinternational PHP2011_Bastian Feder_jQuery's Secrets
international PHP2011_Bastian Feder_jQuery's Secrets
 
Request dispacther interface ppt
Request dispacther interface pptRequest dispacther interface ppt
Request dispacther interface ppt
 
Avinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPressAvinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPress
 
JavaScript and the AST
JavaScript and the ASTJavaScript and the AST
JavaScript and the AST
 
Building Smart Async Functions For Mobile
Building Smart Async Functions For MobileBuilding Smart Async Functions For Mobile
Building Smart Async Functions For Mobile
 

Recently uploaded

Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Victor Rentea
 

Recently uploaded (20)

FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 
Vector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptxVector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptx
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challenges
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Six Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal OntologySix Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal Ontology
 
WSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering DevelopersWSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering Developers
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 

Making Ajax Sexy, JSConf 2010

  • 1. Making Ajax Sexy A Look at Sequential Ajax Patterns
  • 4.
  • 5.
  • 8.
  • 10.
  • 12. WARNING If very bawdy jokes are the sort of thing that offend you or make you uncomfortable in the trousers, you might want to leave now.
  • 13. David Furfero @furf UI Engineer, MLB.com
  • 16. Ajax makes killer web apps possible.
  • 17.
  • 18. Sequential Ajax makes Ajax killer.
  • 19. Sequential Ajax makes Ajax killer.
  • 20.
  • 21. Sexy.js makes Sequential Ajax…Sexy!
  • 22. Sexy.js makes Sequential Ajax…Sexy!
  • 23.
  • 24. Sequential Ajax (Sajax) An Ajax pattern for handling multiple asynchronous requests while maintaining proper callback execution order.
  • 25. Goals of Sequential Ajax (Sajax) • Facilitate combination and manipulation of data from multiple sources • Speed loading of data, scripts, stylesheets using parallel asynchronous HTTP requests • Speed execution of callbacks by allowing them to fire as soon as all previous data is available • Fun
  • 26. Three Flavors of Sajax • Serial request / serial response • Parallel request / random response • Parallel request / serial response
  • 27. In a perfect world…
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42. In a world, where time waits for no man…
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 60. Serial Request / Serial Response // Request an article $.getJSON('/article/1.json', function (article) { // Request the related comments $.getJSON('/article/1/comments.json', function (comments) { // Join the data article.comments = comments; // Render the article and comments render(article); }); });
  • 61. Serial Request / Serial Response • Pros • Relatively readable • Cons • Slower • Nested calls don’t scale
  • 62. Parallel Request / Random Response var i = 0, n = 2, article, comments; $.getJSON('/article/1.json', function (data) { article = data; if (++i == n) { handleComplete(); } }); $.getJSON('/article/1/comments.json', function (data) { comments = data; if (++i == n) { handleComplete(); } }); function handleComplete () { article.comments = comments; render(article); }
  • 63. Parallel Request / Random Response • Pros • Faster • Cons • Heavier • Uglier • Really doesn’t scale
  • 64. Do those patterns look familiar?
  • 66. Sexy is a jQuery plugin A lightweight JavaScript library which provides parallel request/serial response Sequential Ajax (Sajax) functionality and a sleek facade to jQuery’s native Ajax methods.
  • 67. Sexy is a standalone library A lightweight JavaScript library which provides parallel request/serial response Sequential Ajax (Sajax) functionality and a sleek facade to jQuery’s native Ajax methods JavaScript.
  • 68. Sexy is a standalone library A lightweight JavaScript library which provides parallel request/serial response Sequential Ajax (Sajax) functionality and a sleek facade to jQuery’s native Ajax methods JavaScript (using jQuery’s native Ajax methods).
  • 69. Sexy is classy new Sexy() creates a Sexy instance
  • 70. Sexy is classy var s = new Sexy(); s.json(articleUrl, callback) .json(commentsUrl, callback);
  • 71. Sexy is the new Sexy Sexy() also creates a Sexy instance
  • 72. Sexy is the new Sexy Sexy() .json(articleUrl, callback) .json(commentsUrl, callback);
  • 73. Naked Sexy /** * Constructs a new Sexy instance */ function Sexy (cfg) { /** * Allow instantiation without new keyword */ if (!(this instanceof Sexy)) { return new Sexy(cfg); } this.cfgs = []; this.setup(cfg); }
  • 74. Sexy is full of surprises Sexy.fn() also creates a Sexy instance!
  • 75. Sexy is full of surprises Sexy .json(articleUrl, callback) .json(commentsUrl, callback);
  • 76. Naked Sexy /** * Add sexier static methods */ function addStaticMethod (method) { Sexy[method] = function () { return Sexy.prototype[method].apply(new Sexy(), arguments); }; } for (i in Sexy.prototype) { addStaticMethod(i); }
  • 77. I WASN’T KIDDING! There is a bawdy joke coming up VERY soon!
  • 78. I WASN’T KIDDING! If you are pregnant, have a serious heart condition, or giggle uncontrollably at the word “titillate”, you may want to hurry to Track A.
  • 80. Sexy is Your Syntactic Sugar Mama An expressive method for each supported data type: html (url, callback) json (url, callback) jsonp (url, callback) script / js (url, callback) text (url, callback) xml (url, callback) style / css (url, callback)
  • 81. Sexy is Patient Requests can be deferred using an optional 2nd argument. html (url, defer, callback) json (url, defer, callback) jsonp (url, defer, callback) script / js (url, defer, callback) text (url, defer, callback) xml (url, defer, callback) style / css (url, defer, callback)
  • 82. Sexy is Flexible Each method can also take a jQuery settings object. html (settings) json (settings) jsonp (settings) script / js (settings) text (settings) xml (settings) style / css (settings)
  • 83. callback (data, previous, next, status) Each callback receives four arguments: data response data of the current request previous return value of the previous callback next Ajax settings of the next request status result status of the current request
  • 85. Sexy Sexy .json({ url: '/article/1.json', success: function (article) { return article; } }) .json({ url: '/article/1/comments.json', success: function (comments, article) { article.comments = comments; render(article); } });
  • 86. Passing Data with Callbacks Sexy .json({ url: '/article/1.json', success: function (article, previous, next, status) { return article; } }) .json({ url: '/article/1/comments.json', success: function (comments, article, next, status) { article.comments = comments; render(article); } });
  • 87. Sexy-er Sexy .json('/article/1.json', function (article) { return article; }) .json('/article/1/comments.json', function (comments, article) { article.comments = comments; render(article); });
  • 88. Passing Data with Callbacks Sexy .json('/article/1.json', function (article) { return article; }) .json('/article/1/comments.json', function (comments, article) { article.comments = comments; render(article); });
  • 89. Sexy-est Sexy .json('/article/1.json') .json('/article/1/comments.json', function (comments, article) { article.comments = comments; render(article); });
  • 90. Passing Data with Implicit Callbacks Sexy .json('/article/1.json') .json('/article/1/comments.json', function (comments, article) { article.comments = comments; render(article); });
  • 91. Passing Data with Implicit Callbacks Sexy .json('/article/1.json') .js('jquery.js') .json('/article/1/comments.json', function (comments, article) { article.comments = comments; render(article); });
  • 92. Slow it Down Sexy // Authenticate user .json({ type: 'POST', url: 'login', data: { username: 'furf', password: '2s3xy4u' } }) // GET article .json('article/1', true, function (article, previous, nextCfg) { article.author = 'furf'; nextCfg.data = article; }) // POST article .json({ defer: true, type: 'POST', url: 'article/1' }) // De-authenticate user .json('logout', true);
  • 93. Manage <script> Dependencies Sexy .js('jquery.js') .js('jquery-ui.js') .js('jquery-ui-i18n.js') .js('jquery.template.js') .js('mlb.js') .js('mlb.tracking.js') .js('mlb.datagrid.js') .js('mlb.stats.js') .js('mlb.stats.util.js') .js('mlb.stats.widget.js') .js('mlb.stats.sortableplayers.js', function () { mlb.stats.SortablePlayers.init(); });
  • 95. Old and Busted var i = 0, n = 2, article, comments; $.getJSON('/article/1.json', function (data) { article = data; if (++i == n) { handleComplete(); } }); $.getJSON('/article/1/comments.json', function (data) { comments = data; if (++i == n) { handleComplete(); } }); function handleComplete () { article.comments = comments; render(article); }
  • 96. Your Royal Hotness Sexy .json('/article/1.json') .json('/article/1/comments.json', function (comments, article) { article.comments = comments; render(article); });
  • 97. The 4th Stage of Grief ACCEPTANCE There’s no way out now. Strap yourself in. Accept your fate. The bawdy joke is on its way.
  • 99. setup (settings) • Configure subsequent requests of a Sexy instance • Settings can be unset with setup()
  • 100. setup (settings) Sexy .setup({ beforeSend: showLoadingIndicator complete: hideLoadingIndicator }) .json('article.json', diplayArticle) .setup() .json('comments.json', displayComments);
  • 101. Sexy (settings) Sexy({ beforeSend: showLoadingIndicator complete: hideLoadingIndicator }) .json('article.json', diplayArticle) .setup() .json('comments.json', displayComments);
  • 102. bundle (url1, url2, ..., callback) • Client-side JavaScript packaging • Multiple file, single <script> injection • Sequence is preserved
  • 104. JavaScript was too reserved.
  • 106.
  • 107. :) I told you it was bawdy!
  • 108. bundle (url1, url2, ..., callback) • Client-side JavaScript packaging • Multiple file, single <script> injection • Sequence is preserved
  • 109. bundle (url1, url2, ..., callback) Sexy.bundle( 'jquery.js', 'jquery-ui.js', 'jquery-ui-i18n.js', 'jquery.template.js', 'mlb.js', 'mlb.tracking.js', 'mlb.datagrid.js', 'mlb.stats.js', 'mlb.stats.util.js', 'mlb.stats.widget.js', 'mlb.stats.app.sortableplayers.js', function () { mlb.stats.app.SortablePlayers.init(); } );
  • 110. The Kama Sutra of Sexy.js
  • 111. var s = Sexy .js('jquery.min.js') .js('jquery.template.min.js') .css('friends.css') .text('friend.tpl') .json('friends.json', function (data, tpl) { // Send a new request for each friend (executed simultaneously) $.each(data.friends, function (i, friend) { s.json(friend.username + '.json', function (friend, friends) { // Push onto the previously returned array friends.push(friend); // Until the last friend, return the augmented array to the next callback if (i < data.friends.length - 1) { return friends; // When we reach the last friend, render the data and insert into the DOM } else { $.template(tpl, friends).appendTo('#friends'); } }); }); // Instantiate the friends array return []; });
  • 112. Known Issues • Unable to detect error events for remote requests • jsonp (remoteUrl) • script (remoteUrl) • style (remoteUrl) • style (remoteUrl) fires the success callback even if the request was unsuccessful (ie. 404)
  • 113. Parallel Request / Serial Response • Pros • Faster • Cleaner • Prettier • Cons • Sexy.js adds 1.4Kb–4.6Kb to your JavaScript library
  • 114. Parallel Request / Serial Response • Pros • Faster • Cleaner • Prettier • Cons • Sexy.js adds 1.4Kb–4.6Kb to your JavaScript library
  • 115. Can’t get enough? website http://sexyjs.com slideshow http://www.slideshare.net/furf download http://github.com/furf/Sexy twitter @sexyjs or @furf email furf@furf.com
  • 116. @thanks! jsconf, voodootikigod, jquery, jeresig, jaubourg, dimak25, subchild, brandonaaron, yoni, jsconfeu, getify, maraksquires, attricetta, and you!

Editor's Notes

  1. good afternoon welcome to making ajax sexy
  2. feel free to be geeky as we go along, my code is available on github
  3. Also, if you&amp;#x2019;d like to read some hastily scribed documentation, i have a website at sexyjs.com, but i have to warn you ...
  4. it&amp;#x2019;s been deemed too risque for the workplace...
  5. by this guy... if you think it&amp;#x2019;s NSFW but still want to try it
  6. i&amp;#x2019;ve prepared another website for you
  7. i&amp;#x2019;ve prepared another website for you
  8. it&amp;#x2019;s even compatible with navigator 4.
  9. okay... let me look at my notes for a second
  10. okay... let me look at my notes for a second
  11. OK. Let&amp;#x2019;s begin. Some of you might remember me from jsconf.eu. For those that don&amp;#x2019;t, my name is Dave Furfero. I&amp;#x2019;m a UI Engineer at MLB.com, long-time JavaScripter, long-form improviser, and all-around lovable guy. I&amp;#x2019;m known as furf all around the world and the Internets.
  12. okay let&amp;#x2019;s get started
  13. I&amp;#x2019;ve come to talk to you about your never-ending thirst for asynchronous data, some Sequential Ajax patterns for loading and combining your data, and a little library I wrote called Sexy which makes it so so simple.
  14. I&amp;#x2019;d guess that about 95% of the people in this room are working on the Internet&amp;#x2019;s next killer app. I&amp;#x2019;d also wager that 95% of those people are using Ajax to load data and that 95% of those people are loading data from more than one service. And I would venture that 95% of those people would like that data to interact in a meaningful way.
  15. As we work with a larger number and greater variety of data sources, and we&amp;#x2019;re no longer served by the one request/one callback paradigm, we need to evolve new patterns for dealing with the unpredictability of Ajax. Sequential Ajax helps you mix and mash asynchronous data from various sources, local and remote, with predictable results.
  16. As we work with a larger number and greater variety of data sources, and we&amp;#x2019;re no longer served by the one request/one callback paradigm, we need to evolve new patterns for dealing with the unpredictability of Ajax. Sequential Ajax helps you mix and mash asynchronous data from various sources, local and remote, with predictable results.
  17. And Sexy? Sexy makes it look good. Damn good.
  18. And Sexy? Sexy makes it look good. Damn good.
  19. Sequential Ajax is any of a number of patterns for writing Ajax that allow you to fire multiple requests, receive the data, and hopefully before they are finished allow you to meaningfully manipulate that data
  20. Sajax comes in three flavors. Two of them get the job done. One of them gets the job done Sexily
  21. Let&amp;#x2019;s say we fire off three asynchronous requests, the server or servers do their thing, and send back the data. In this scenario, our data returns in the specified order and we execute each callback as soon as its data is available ready. Even if the others have yet to return.
  22. Let&amp;#x2019;s say we fire off three asynchronous requests, the server or servers do their thing, and send back the data. In this scenario, our data returns in the specified order and we execute each callback as soon as its data is available ready. Even if the others have yet to return.
  23. Let&amp;#x2019;s say we fire off three asynchronous requests, the server or servers do their thing, and send back the data. In this scenario, our data returns in the specified order and we execute each callback as soon as its data is available ready. Even if the others have yet to return.
  24. Let&amp;#x2019;s say we fire off three asynchronous requests, the server or servers do their thing, and send back the data. In this scenario, our data returns in the specified order and we execute each callback as soon as its data is available ready. Even if the others have yet to return.
  25. Let&amp;#x2019;s say we fire off three asynchronous requests, the server or servers do their thing, and send back the data. In this scenario, our data returns in the specified order and we execute each callback as soon as its data is available ready. Even if the others have yet to return.
  26. Let&amp;#x2019;s say we fire off three asynchronous requests, the server or servers do their thing, and send back the data. In this scenario, our data returns in the specified order and we execute each callback as soon as its data is available ready. Even if the others have yet to return.
  27. Let&amp;#x2019;s say we fire off three asynchronous requests, the server or servers do their thing, and send back the data. In this scenario, our data returns in the specified order and we execute each callback as soon as its data is available ready. Even if the others have yet to return.
  28. Let&amp;#x2019;s say we fire off three asynchronous requests, the server or servers do their thing, and send back the data. In this scenario, our data returns in the specified order and we execute each callback as soon as its data is available ready. Even if the others have yet to return.
  29. Let&amp;#x2019;s fire off three asynchronous requests, the server or servers do their thing, and send back the data. We have no control over the timing. In this scenario, our data returns in the specified order and we execute each callback as soon as its data is available ready. Even if the others have yet to return.
  30. Let&amp;#x2019;s say we fire off three asynchronous requests, the server or servers do their thing, and send back the data. In this scenario, our data returns in the specified order and we execute each callback as soon as its data is available ready. Even if the others have yet to return.
  31. Let&amp;#x2019;s say we fire off three asynchronous requests, the server or servers do their thing, and send back the data. In this scenario, our data returns in the specified order and we execute each callback as soon as its data is available ready. Even if the others have yet to return.
  32. Let&amp;#x2019;s say we fire off three asynchronous requests, the server or servers do their thing, and send back the data. In this scenario, our data returns in the specified order and we execute each callback as soon as its data is available ready. Even if the others have yet to return.
  33. Let&amp;#x2019;s say we fire off three asynchronous requests, the server or servers do their thing, and send back the data. In this scenario, our data returns in the specified order and we execute each callback as soon as its data is available ready. Even if the others have yet to return.
  34. Let&amp;#x2019;s say we fire off three asynchronous requests, the server or servers do their thing, and send back the data. In this scenario, our data returns in the specified order and we execute each callback as soon as its data is available ready. Even if the others have yet to return.
  35. In this scenario, our servers will return the data out of order. Note how Callbacks B &amp; C wait for the return of Callback A before firing.
  36. Let&amp;#x2019;s start by looking at some common anti-patterns and see if they look familiar. If they do, you may want to stick around...
  37. The first anti-pattern is serial request/serial response. Successive Ajax requests are nested inside callbacks and executed upon completion of the preceding request. Previous data is accessible to each callback via closure. WALKTHRU...
  38. The second anti-pattern is parallel request / random response. This pattern is quicker because requests are made simultaneously, but usually requires a counter and a common callback which is fired once all requests are complete.
  39. As I mentioned, this pattern is faster, but the code quickly grows bloated, crufty, and significantly harder to maintain.
  40. Sexy.js uses the 3rd Sajax pattern: parallel request/serial response. Requests made in parallel for speed and then by wrapping and eventing callback functions, Sexy ensures proper callback execution order every time.
  41. Originally written as a jQuery plugin, Sexy is now available as a standalone library using jQuery&amp;#x2019;s Ajax. Yay, open source! Both the plugin and standalone pass jQuery as an adapter so I encourage anyone interested in porting Sexy to other libs to do so. I&amp;#x2019;d be happy to help.
  42. Originally written as a jQuery plugin, Sexy is now available as a standalone library using jQuery&amp;#x2019;s Ajax. Yay, open source! Both the plugin and standalone pass jQuery as an adapter so I encourage anyone interested in porting Sexy to other libs to do so. I&amp;#x2019;d be happy to help.
  43. Sexy is a class. New Sexy chains can be constructed by calling new Sexy. And then requests can be made using the returned instance.
  44. Sexy is a class. New Sexy chains can be constructed by calling new Sexy. And then requests can be made using the returned instance.
  45. But I didn&amp;#x2019;t think that was Sexy enough. So I did away with the new keyword.
  46. But I didn&amp;#x2019;t think that was Sexy enough. So I did away with the new keyword.
  47. Let&amp;#x2019;s look inside the Sexy constructor. Sexy checks the instanceof this, which if used with the new keyword would be Sexy. If it&amp;#x2019;s not Sexy, then it was called without the new keyword, so we call it with the new keyword and return the new instance.
  48. Still not sexy enough! A Sexy object can also be instantiated just by calling any one of Sexy&amp;#x2019;s instance methods as a static method of Sexy. (I just thought the extra parentheses were ugly.)
  49. Still not sexy enough! A Sexy object can also be instantiated by calling any one of Sexy&amp;#x2019;s instance methods as a static method of Sexy. (I just thought the extra parentheses were ugly.)
  50. Looking back inside the code, you can see that we just loop the prototypal methods and add static methods that apply the desired method in the scope of a new Sexy instance. Since the API is 100% chainable, returning the method&amp;#x2019;s return value, returns the instance. To which we can chain more calls.
  51. okay, let&amp;#x2019;s take a look at Sexy&amp;#x2019;s api. sexy was designed to be expressive and chainable so that the code looks like what the code does.
  52. okay, let&amp;#x2019;s take a look at Sexy&amp;#x2019;s api. sexy was designed to be expressive and chainable so that the code looks like what the code does.
  53. okay, let&amp;#x2019;s take a look at Sexy&amp;#x2019;s api. sexy was designed to be expressive and chainable so that the code looks like what the code does.
  54. A method for each supported data type. These types reflect the Sexy&amp;#x2019;s jQuery roots with the exception of style, which adds stylesheet support and was added for &amp;#x201C;completeness&amp;#x201D;. Helpful when bootstrapping an app w Sexy. Script and Style have aliases, js and css. This is the shorthand form, and takes a URL and a callback as arguments.
  55. An optional 2nd argument will let you defer the request until the previous request has completed. You will see in a few slides how this allows us to handle server authentication and modify successive requests.
  56. In its verbose form, each method can also take a jQuery-like configuration object, for when you want to POST data or add an error callbacks, etc. Defer is also a configurable property of the configuration object
  57. look up dummy :)
  58. Here we can see two calls being chained. The requests are made simultaneously and the callbacks executed in the specified order. This is an example of the verbose syntax using configuration objects. Note the second argument of the second callback.
  59. This is how Sexy shares data. The return value of a success callback is cached when it is fired and then appended to the arguments of the following success callback when it is ready.
  60. But big configuration objects aren&amp;#x2019;t always sexy. So here&amp;#x2019;s the brief syntax which takes url and callback.
  61. Once again note the sharing of article between callbacks. But notice how we&amp;#x2019;re just returning article. THis seems like something we&amp;#x2019;ll do quite frequently, so...
  62. Sexy provides implicit callbacks. If you do not specify a callback, a pass thru function will be used to pass your results immediately to the next callback.
  63. Note article just automagically appears as an argument to our second callback.
  64. Since scripts and styles don&amp;#x2019;t usually bring back data we want to interpret, if there is no callback, we pass thru the return value of the previous callback. You can stack as many as you want in there and the article will still get through. (of course that decreases the readability and should be avoided)
  65. Using defer we can force subsequent calls to behave in a serial request/serial response pattern, but we still benefit from the enhanced data availablility. Once logged in, we get the article, we can then manipulate it and pass it to the next request as its data property for posting before logging out.
  66. Sexy (like jQuery) loads local scripts as text and inserts them into the DOM, so managing script dependencies is as easy as writing a grocery list, a Secy grocery list.
  67. now its time for a Sexy Sajax makeover...
  68. Remember our parallel request / random response anti-pattern? Let&amp;#x2019;s add a little sexy...
  69. and voila! all rise for your royal hotness.
  70. Here you can see that we want to display a loading indicator while the article loads, but its not so important while the comments load, so by passing any falsy value you can unset the previous config
  71. you can save yourself some keystrokes by passing your initial setup to the constructor
  72. Sexy also provides a sort of client-side Javascript packaging tool called Sexy.bundle...
  73. Bundle makes multiple requests and a single DOM insertion and then executes an optional callback. Because of the way sexy is designed, the script sequence and therefore your dependencies are preserved.
  74. Bundle makes multiple requests and a single DOM insertion and then executes an optional callback. Because of the way sexy is designed, the script sequence and therefore your dependencies are preserved.
  75. Bundle makes multiple requests and a single DOM insertion and then executes an optional callback. Because of the way sexy is designed, the script sequence and therefore your dependencies are preserved.
  76. Bundle makes multiple requests and a single DOM insertion and then executes an optional callback. Because of the way sexy is designed, the script sequence and therefore your dependencies are preserved.
  77. Bundle makes multiple requests and a single DOM insertion and then executes an optional callback. Because of the way sexy is designed, the script sequence and therefore your dependencies are preserved.
  78. Bundle makes multiple requests and a single DOM insertion and then executes an optional callback. Because of the way sexy is designed, the script sequence and therefore your dependencies are preserved.
  79. Bundle makes multiple requests and a single DOM insertion and then executes an optional callback. Because of the way sexy is designed, the script sequence and therefore your dependencies are preserved.