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

JavaScript Promise

JavaScript Promise

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”