Upgrade to Pro — share decks privately, control downloads, hide ads and more …

JavaScript Promise

JavaScript Promise

Avatar for Joseph Chiang

Joseph Chiang

June 04, 2015
Tweet

More Decks by Joseph Chiang

Other Decks in Technology

Transcript

  1. WHY Render multiple dropdowns
 with non-duplicate AJAX calls Need AJAX

    call? Any same URL is processing? Already has
 cached data? All selected values are matched … The reason I decided to investigate Promise…
  2. Asynchronous Code Everywhere in JavaScript setTimeout(function () {! // do

    something ! }, 1000); require(['lodash', 'jquery'], ! function (_, $) {! // do something with lodash and jQuery! }! ); $('form').on('submit', function (e) {! // do something when user submit form! });! ! $('img').on('load', function (e) {! // do something when img loaded! });! ! $('img').on('error', function (e) {! // do something when img fails loading! }); $.ajax('/terms.json', function () {! // do something after api data ! // being loaded! }) Delay RequireJS AJAX DOM Events fs.readFile('foo.txt', function () {! // do something after foo.txt ! // being loaded! }) Node.js
  3. Asynchronous Code Everywhere is Callback setTimeout(function () {! // do

    something ! }, 1000); require(['lodash', 'jquery'], ! function (_, $) {! // do something with lodash and jQuery! }! ); $('form').on('submit', function () {! // do something when user submit form! });! ! $('img').on('load', function () {! // do something when img loaded! });! ! $('img').on('error', function () {! // do something when img fails loading! }); $.ajax('/terms.json', function () {! // do something after api data ! // being loaded! }) Delay RequireJS AJAX DOM Events fs.readFile('foo.txt', function () {! // do something after foo.txt ! // being loaded! }) Node.js
  4. Asynchronous Code Everywhere is Callback setTimeout(function () {! // do

    something ! }, 1000); require(['lodash', 'jquery'], ! function (_, $) {! // do something with lodash and jQuery! }! ); $('form').on('submit', function () {! // do something when user submit form! });! ! $('img').on('load', function () {! // do something when img loaded! });! ! $('img').on('error', function () {! // do something when img fails loading! }); $.ajax('/terms.json', function () {! // do something after api data ! // being loaded! }) Delay RequireJS AJAX DOM Events fs.readFile('foo.txt', function () {! // do something after foo.txt ! // being loaded! }) Node.js Nothing wrong with Callback Use it when your scenario is simple
  5. Sequencial Requests function GitHubber() {}! ! GitHubber.prototype.getUserRepos = function (name,

    callback) {! ! // Authorisation pass?! var url = '/api/authorize';
 makeRequest(url, function (data, success) {! // (Omit) Callback if it fails… ! // Get user ID by name! url = '/api/getUserInfo/' + name + '?token=' + data.token;! makeRequest(url, function (data, success) {! // (Omit) Callback if it fails… ! // Get user's repo by user ID! url = '/api/getUserRepos?token=…&uid=' + data.uid;! makeRequest(url, function (data, success) {! // (Omit) Callback if it fails… ! // Finally success
 callback(data, true);
 });! });! });! }; API Authorisation Get User ID Get User Repos Token Token + User ID Callback Hell GitHubber#getUserRepos Question: How will you refactor it?
  6. function GitHubber(name) {! this.name = name;! this.token = null;! this.id

    = null;! this.repos = [];! this.steps = ['_authorise', '_getUserInfo', '_getUserRepos'];! }! ! var proto = {! _authorise: function () {! var url = '/api/authorise';! makeRequest(url, function (data, success) {! this.token = data.token;! this.emit('success', [‘_authorise']);! });! },! _getUserInfo: function () {! var url = '/api/getUserInfo/' + this.name + 
 '?token=' + data.token;! makeRequest(url, function (data, success) {! this.id = data.id;! this.emit('success', [‘_getUserInfo']);! });! },! _getUserRepos: function () {! var url = '/api/getRepos/?uid=' + this.id + ! '?token=' + data.token;! makeRequest(url, function (data, success) {! this.repos = data.repos;! this.emit('success', [‘_getUserRepos', this.repos]);! });! },! getUserRepos: function (callback) {! var that = this;! that.on('success', function (e, method) {! var offset = that.steps.indexOf(method);! if (offset !== that.steps.length - 1) { // Other steps! that[that.steps[offset + 1]];! } else { // _getUserRepos! callback(that.repos); ! }! });! that[that.steps[0]]();! }! }; My Solution Before understanding Promise • Break callbacks into methods with semantic naming • Exchange data with instance variables • Make use of custom events I am a big fan of Custom Events Wolfy87/EventEmitter
  7. function GitHubber(name) {! this.name = name;! this.token = null;! this.id

    = null;! this.repos = [];! this.steps = ['_authorise', '_getUserInfo', '_getUserRepos'];! }! ! var proto = {! _authorise: function () {! var url = '/api/authorise';! makeRequest(url, function (data, success) {! this.token = data.token;! this.emit('success', [‘_authorise']);! });! },! _getUserInfo: function () {! var url = '/api/getUserInfo/' + this.name + 
 '?token=' + data.token;! makeRequest(url, function (data, success) {! this.id = data.id;! this.emit('success', [‘_getUserInfo']);! });! },! _getUserRepos: function () {! var url = '/api/getRepos/?uid=' + this.id + ! '?token=' + data.token;! makeRequest(url, function (data, success) {! this.repos = data.repos;! this.emit('success', [‘_getUserRepos', this.repos]);! });! },! getUserRepos: function (callback) {! var that = this;! that.on('success', function (e, method) {! var offset = that.steps.indexOf(method);! if (offset !== that.steps.length - 1) { // Other steps! that[that.steps[offset + 1]];! } else { // _getUserRepos! callback(that.repos); ! }! });! that[that.steps[0]]();! }! }; My Solution Before understanding Promise • Break callbacks into methods with semantic naming • Exchange data with instance variables • Make use of custom events I am a big fan of Custom Events Wolfy87/EventEmitter Better but still not straightforward Need read carefully to understand the trick ex. sequence, error handling, and parallel events
  8. PROMISE • A Programming Pattern • Specialise on Asynchronous Code

    • Better Maintainability • Easier for Scaling NOT another JavaScript framework
  9. Create A Promise .then Returns promise immediately getUserRepos: function ()

    { ! ! ! }, “Our workers will do all tasks for you” “Keep the ticket for now. I promise you will get 
 a fulfilled or rejected result” • Pending • Fulfilled: that.repoDeferred.resolve(data.repos) • Rejected: that.repoDeferred.reject(‘service unavailable’) that.repoDeferred = new $.Deferred(); that.asyncTasks() return that.repoDeferred.promise(); “This task may take a while”
  10. Use Promise .then gitHubber.getUserRepos() 
 ! Chain-able fulfilled callback Rejected

    callback Promise is still callback • .then(fnFulfilled, fnRejected) • .done(fnFulfilled) • .fail(fnRejected) .then(function (repos) {}) .catch(function (msg) {});
  11. Batch Promise .then var deferreds = [ gitHubber.getUserRepos(), gitHubber.getUserProfile(), gitHubber.getOrgaizations()

    ]; ! $.when(deferreds) .done(fnFulfilled) .fail(fnRejected); All succeeds Execute multiple promises together One or more fails Promises Array
  12. 1st Refactoring Not attractive… :( function GitHubber(name) {! this.name =

    name;! this.repos = [];! this.deferreds = {};! }! ! var proto = {! _authorise: function () {! var that = this,! url = '/api/authorise';! ! that.deferreds._authorise = $.Deferreds();! $.ajax(url, function (data) {! that.deferreds._authorise.resolve(data);! });! return that.deferreds._authorise.promise();! },! _getUserInfo: function () {! var that = this,! url = '/api/getUserInfo/' + this.name + '?token=' + data.token;! ! that.deferreds._getUserInfo = $.Deferreds();! $.ajax(url, function (data) {! that.deferreds._getUserInfo.resolve(data.id);! });! return that.deferreds._getUserInfo.promise();! },! _getUserRepos: function () {! var that = this,! url = '/api/getRepos/?uid=' + this.id + '?token=' + data.token;! ! that.deferreds._getUserRepos = $.Deferreds();! $.ajax(url, function (data) {! that.deferreds._getUserRepos.resolve(data.repos);! });!
  13. function GitHubber (name) {! this.name = name;! this.token = null;!

    that.repos = [];! }! ! var proto = {! getUserRepos: function (callback) {! var that = this,! deferred = $.Deferred();! ! if (that.repos.length) {! deferred.resolve(that.repos);! return;! }! ! $.ajax('/api/authorise')! .then(function (data) {! that.token = data.token;! return $.ajax('/api/getUserInfo/' + that.name +'?token=' + data.token);! })! .then(function (data) {! return $.ajax('/api/getRepos/?uid=' + data.uid + '?token=' + that.token);! })! .then(function (data) {! that.repos = data.repos;! deferred.resolve(data.repos);! });! ! return deferred.promise();! }! }; 2nd: jQuery Promises $.ajax $.when $.getJSON $.ajax() is also a promise object!
  14. function GitHubber (name) {! this.name = name;! this.token = null;!

    that.repos = [];! }! ! var proto = {! getUserRepos: function (callback) {! var that = this,! deferred = $.Deferred();! ! if (that.repos.length) {! deferred.resolve(that.repos);! return;! }! ! $.ajax('/api/authorise')! .then(function (data) {! that.token = data.token;! return $.ajax('/api/getUserInfo/' + that.name +'?token=' + data.token);! })! .then(function (data) {! return $.ajax('/api/getRepos/?uid=' + data.uid + '?token=' + that.token);! })! .then(function (data) {! that.repos = data.repos;! deferred.resolve(data.repos);! });! ! return deferred.promise();! }! }; 2nd: jQuery Promises $.ajax $.when $.getJSON $.ajax() is also a promise object! You can reduce huge amount of code by chaining & wrapping promise object properly
  15. Promise v.s. Callback Why Promise? var promise = $.ajax(url);! promise.done(callback);

    $.ajax(url, callback) Promise Callback • Portability - async task must be fulfilled or rejected in delegated methods. • Consistency - .resolve(), .reject() , .then(), .done(), .catch(), rejected, fulfilled, pending • Chaining - .then() makes sequential tasks easier to execute • Straightforward - .then() makes our code easier to read “First-class API for asynchronous tasks”
  16. With Promise… .then • We defines sequence in a very

    straightforward way (.then) • Built-in error handling API (.fail) • Batch execute parallel tasks easily (Promise.all) Solved a lot of async design issues
  17. Promise & jQuery jQuery’s Deferreds aren't Promise/A+ compliant. 
 Please

    avoid to use if you want to use Promise extensively. $.Deferred $.when
  18. Promise in Node.js Node.js added native promise in stable version

    0.12 JVVRUIKVJWDEQORTQOKUGUCRNWURTQOKUGUURGEKUUWGU … comes with memory leak
  19. Libraries For both Browser and Node.js • Q.js 
 A

    tool for creating and composing asynchronous promises in JavaScript • RSVP.js
 A lightweight library that provides tools for organising asynchronous code • when.js
 A solid, fast Promises/A+ and when() implementation, plus other async goodies. • bluebird (most popular)
 Bluebird is a full featured promise library with unmatched performance.
  20. Libraries For both Browser and Node.js • Q.js 
 A

    tool for creating and composing asynchronous promises in JavaScript • RSVP.js
 A lightweight library that provides tools for organising asynchronous code • when.js
 A solid, fast Promises/A+ and when() implementation, plus other async goodies. • bluebird (most popular)
 Bluebird is a full featured promise library with unmatched performance. Currently you probably need library for polyfills Use jQuery Deferred with awareness
  21. Q & A • Promise - A Programming Pattern •

    Specialise on Asynchronous Code • Better Maintainability • Easier for Scaling “First-class API for asynchronous tasks”