SlideShare a Scribd company logo
Callbacks, Promises, and Coroutines
                  (oh my!)


     Asynchronous Programming
        Patterns in JavaScript


                Domenic Denicola
           http://domenicdenicola.com
                @domenicdenicola
In non-web languages,
most of the code we write is synchronous.

                aka blocking
Console.WriteLine("What is your name?");
string name = Console.ReadLine();
Console.WriteLine("Hello, " + name);
var fileNames = Directory.EnumerateFiles("C:");

foreach (var fileName in fileNames)
{
  using (var f = File.Open(fileName, FileMode.Open))
  {
    Console.WriteLine(fileName + " " + f.Length);
  }
}
using (var client = new WebClient())
{
  string html =
client.DownloadString("http://news.ycombinator.com");

    Console.WriteLine(html.Contains("Google"));
    Console.WriteLine(html.Contains("Microsoft"));
    Console.WriteLine(html.Contains("Apple"));
}
Thread.Start                               BackgroundWorker


               Control.InvokeRequired



        This often causes us some pain…

       … but hey, there’s always threads!

                       Dispatcher.Invoke



ThreadPool

                                              .AsParallel()
Q: What are these threads doing, most of the time?


A: waiting
WTF!?
In JavaScript, we do things differently.
There’s only one thread in JavaScript,
so we use that thread to get stuff done.
OK, let’s talk about…
•   The event loop
•   Callbacks
•   Promises
•   Coroutines
THE EVENT LOOP
You’ve seen event loops before:
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)
{
  MSG msg;
  while (GetMessage(&msg, NULL, 0, 0) > 0)
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return msg.wParam;
}
this.btnOK.Click += this.btnOK_Click;


private void btnOK_Click(object sender,
                         EventArgs e)
{
  // ...
}
$("#ok-button").click(function () {
    // ...
});

setTimeout(function () {
    // ...
}, 100);

$.get("http://example.com", function (result) {
    // ...
});
Some event loop subtleties
•   Yielding
•   Async’s not sync
•   Errors
•   It’s not magic
Event Loop Subtleties

   Yielding
   console.log("1");

   $.get("/echo/2", function (result) {
     console.log(result);
   });

   console.log("3");

   // 1, 3, 2
Event Loop Subtleties

   Async’s not sync
   var hi = null;

   $.get("/echo/hi", function (result) {
     hi = result;
   });

   console.log(hi);

   // null
Event Loop Subtleties

   Errors
   console.log("About to get the website...");

   $.ajax("http://sometimesdown.example.com", {
     success: function (result) {
        console.log(result);
     },
     error: function () {
        throw new Error("Error getting the website");
     }
   });

   console.log("Continuing about my business...");
Event Loop Subtleties

   It’s not magic
   function fib(n) {
       return n < 2 ? 1 : fib(n-2) + fib(n-1);
   }

   console.log("1");

   setTimeout(function () {
      console.log("2");
   }, 100);

   fib(40);

   // 1 ... 15 seconds later ... 2

http://teddziuba.com/2011/10/node-js-is-cancer.html
The event loop is tricky… but powerful.
CALLBACKS
What we’ve seen so far has been doing
  asynchronicity through callbacks.
Callbacks are OK for simple operations, but
 force us into continuation passing style.
Recurring StackOverflow question:
function getY() {
  var y;
  $.get("/gety", function (jsonData) {
    y = jsonData.y;
  });
  return y;
}
                      Why doesn’t it work???
var x = 5;
var y = getY();

console.log(x + y);
After getting our data, we have to do
 everything else in a continuation:
function getY(continueWith) {
    $.get("/gety", function (jsonData) {
      continueWith(jsonData.y);
    });
}


var x = 5;
getY(function (y) {
    console.log(x + y);
});
CPS Headaches
• Doing things in sequence is hard
• Doing things in parallel is harder
• Errors get lost easily
CPS Headaches

  Doing things in sequence is hard
  $("#button").click(function () {
    promptUserForTwitterHandle(function (handle) {
      twitter.getTweetsFor(handle, function (tweets) {
        ui.show(tweets);
      });
    });
  });
CPS Headaches

  Doing things in parallel is harder
  var tweets, answers, checkins;

  twitter.getTweetsFor("domenicdenicola", function (result) {
    tweets = result;
    somethingFinished();
  });
  stackOverflow.getAnswersFor("Domenic", function (result) {
    answers = result;
    somethingFinished();
  });
  fourSquare.getCheckinsBy("Domenic", function (result) {
    checkins = result;
    somethingFinished();
  });
CPS Headaches

  Doing things in parallel is harder
  var finishedSoFar = 0;

  function somethingFinished() {
    if (++finishedSoFar === 3) {
      ui.show(tweets, answers, checkins);
    }
  }
CPS Headaches

  Errors get lost easily
  function getTotalFileLengths(path, callback) {
    fs.readdir(path, function (err, fileNames) {
      var total = 0;

       var finishedSoFar = 0;
       function finished() {
         if (++finishedSoFar === fileNames.length) {
           callback(total);
         }
       }

        fileNames.forEach(function (fileName) {
          fs.readFile(fileName, function (err, file) {
            total += file.length;
            finished();
          });
        });
      });
  }
You could write your own library to make this nicer…
function parallel(actions, callback) {
  var results = [];
  function finished(result) {
    results.push(result);
    if (results.length === actions.length) {
        callback(results);
    }
  }

    actions.forEach(function (action) {
      action(finished);
    });
}
parallel([
  function (cb) {
     twitter.getTweetsFor("domenicdenicola", cb);
  },
  function (cb) {
     stackOverflow.getAnswersFor("Domenic", cb);
  },
  function (cb) {
     fourSquare.getCheckinsFor("Domenic", cb);
  }
], function (results) {
  console.log("tweets = ", results[0]);
  console.log("answers = ", results[1]);
  console.log("checkins = ", results[2]);
});
And in fact many people have:




https://github.com/joyent/node/wiki/modules#wiki-async-flow
The best of these (IMO) are based on
  an abstraction called “promises”
PROMISES
Un-inverts the chain of responsibility:
instead of calling a passed callback, return a promise.
addWithCallback(a, b, function (result) {
  assert.equal(result, a + b);
});

var promise = addWithPromise(a, b);

promise.then(function (result) {
  assert.equal(result, a + b);
});
Why promises are awesome

• Cleaner method signatures
• Uniform return/error semantics
• Easy composition
• Easy sequential/parallel join
• Always async
• Exception-style error bubbling
Promises are Awesome

  Cleaner method signatures
  Uniform return/error semantics
  $.get(
    url,
    [data],
    [success(data, status, xhr)],
    [dataType]
  )
Promises are Awesome

  Cleaner method signatures
  Uniform return/error semantics
  $.ajax(url, settings)

  settings.success(data, status, xhr)
  settings.error(xhr, status, errorThrown)
  settings.complete(xhr, status)
Promises are Awesome

  Cleaner method signatures
  Uniform return/error semantics
  fs.open(
    path,
    flags,
    [mode],
    [callback(error, file)]
  )
Promises are Awesome

  Cleaner method signatures
  Uniform return/error semantics
  fs.write(
    file,
    buffer,
    offset,
    length,
    position,
    [callback(error, written, buffer)]
  )
Promises are Awesome

  Cleaner method signatures
  Uniform return/error semantics
  getAsPromise(url, [data], [dataType]).then(
    function onFulfilled(result) {
       var data = result.data;
       var status = result.status;
       var xhr = result.xhr;
    },
    function onBroken(error) {
       console.error("Couldn't get", error);
    }
  );
Promises are Awesome

  Easy composition

  function getUser(userName, onSuccess, onError) {
    $.ajax("/user?" + userName, {
      success: onSuccess,
      error: onError
    });
  }
Promises are Awesome

  Easy composition

  function getUser(userName) {
    return getAsPromise("/user?" + userName);
  }
Promises are Awesome

  Easy composition

  function getFirstName(userName, onSuccess, onError) {
    $.ajax("/user?" + userName, {
      success: function successProxy(data) {
         onSuccess(data.firstName);
      },
      error: onError
    });
  }
Promises are Awesome

  Easy composition

  function getFirstName(userName) {
    return getAsPromise("/user?" + userName)
             .get("firstName");
  }
Promises are Awesome

  Easy sequential join

  $("#button").click(function () {
    promptUserForTwitterHandle(function (handle) {
      twitter.getTweetsFor(handle, function (tweets) {
        ui.show(tweets);
      });
    });
  });
Promises are Awesome

  Easy sequential join

  $("#button").clickPromise()
    .then(promptUserForTwitterHandle)
    .then(twitter.getTweetsFor)
    .then(ui.show);
Promises are Awesome

  Easy parallel join

  var tweets, answers, checkins;

  twitter.getTweetsFor("domenicdenicola", function (result) {
    tweets = result;
    somethingFinished();
  });
  stackOverflow.getAnswersFor("Domenic", function (result) {
    answers = result;
    somethingFinished();
  });
  fourSquare.getCheckinsBy("Domenic", function (result) {
    checkins = result;
    somethingFinished();
  });
Promises are Awesome

  Easy parallel join

  Q.all([
    twitter.getTweetsFor("domenicdenicola"),
    stackOverflow.getAnswersFor("Domenic"),
    fourSquare.getCheckinsBy("Domenic")
  ]).then(function (results) {
    console.log(results[0], results[1], results[2]);
  });
Promises are Awesome

  Easy parallel join

  Q.all([
    twitter.getTweetsFor("domenicdenicola"),
    stackOverflow.getAnswersFor("Domenic"),
    fourSquare.getCheckinsBy("Domenic")
  ]).spread(function (tweets, answers, checkins) {
    console.log(tweets, answers, checkins);
  });
Promises are Awesome

  Always async

  function getUser(userName, onSuccess, onError) {
    if (cache.has(userName)) {
      onSuccess(cache.get(userName));
    } else {
      $.ajax("/user?" + userName, {
        success: onSuccess,
        error: onError
      });
    }
  }
Promises are Awesome

  Always async

  console.log("1");

  getUser("ddenicola", function (user) {
    console.log(user.firstName);
  });

  console.log("2");

  // 1, 2, Domenic
Promises are Awesome

  Always async

  console.log("1");

  getUser("ddenicola", function (user) {
    console.log(user.firstName);
  });

  console.log("2");

  // 1, Domenic, 2
Promises are Awesome

  Always async

  function getUser(userName) {
    if (cache.has(userName)) {
      return Q.ref(cache.get(userName));
    } else {
      return getWithPromise("/user?" + userName);
    }
  }
Promises are Awesome

  Always async

  console.log("1");

  getUser("ddenicola").then(function (user) {
    console.log(user.firstName);
  });

  console.log("2");

  // 1, 2, Domenic (every time)
Promises are Awesome

  Exception-style error bubbling

  getUser("Domenic", function (user) {
    getBestFriend(user, function (friend) {
      ui.showBestFriend(friend);
    });
  });
Promises are Awesome

  Exception-style error bubbling

  getUser("Domenic", function (err, user) {
    if (err) {
      ui.error(err);
    } else {
      getBestFriend(user, function (err, friend) {
        if (err) {
          ui.error(err);
        } else {
          ui.showBestFriend(friend);
        }
      });
    }
  });
Promises are Awesome

  Exception-style error bubbling

  getUser("Domenic")
    .then(getBestFriend, ui.error)
    .then(ui.showBestFriend, ui.error);
Promises are Awesome

  Exception-style error bubbling

  getUser("Domenic")
    .then(getBestFriend)
    .then(ui.showBestFriend, ui.error);
Promises are Awesome

  Exception-style error bubbling

  ui.startSpinner();
  getUser("Domenic")
    .then(getBestFriend)
    .then(
       function (friend) {
          ui.showBestFriend(friend);
          ui.stopSpinner();
       },
       function (error) {
          ui.error(error);
          ui.stopSpinner();
       }
    );
Promises are Awesome

  Exception-style error bubbling

  ui.startSpinner();
  getUser("Domenic")
    .then(getBestFriend)
    .then(ui.showBestFriend, ui.error)
    .fin(ui.stopSpinner);
Promises are Awesome

  Exception-style error bubbling

  function getBestFriendAndDontGiveUp(user) {
    return getUser(user).then(
      getBestFriend,
      function (error) {
        if (error instanceof TemporaryNetworkError) {
          console.log("Retrying after error", error);
          return getBestFriendAndDontGiveUp(user);
        }
        throw error;
      });
  }
Sounds great. How do I get in on this action?
Use Q
•   By Kris Kowal, @kriskowal
•   https://github.com/kriskowal/q
•   Can consume promises from jQuery etc.
•   Implements various CommonJS standards
If you’re already using jQuery’s promises, switch to Q:


    https://github.com/kriskowal/q/wiki/jQuery
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patterns in JavaScript
Creating promises with Q

   Fulfilling promises
   // We have:
   setTimeout(doSomething, 1000);

   // We want:
   delay(1000).then(doSomething);
Creating promises with Q

   Fulfilling promises
   function delay(ms) {
     var deferred = Q.defer();
     setTimeout(deferred.resolve, ms);
     return deferred.promise;
   }

   delay(1000).then(doSomething);
Creating promises with Q

   Breaking promises
   function getWithTimeout(url, ms, onSuccess, onError) {
     var isTimedOut = false, isHttpErrored = false;

       setTimeout(function () {
         if (!isHttpErrored) {
           isTimedOut = true;
           onError(new Error("timed out"));
         }
       }, ms);

       $.ajax(url, {
         success: function (result) {
           if (!isTimedOut) { onSuccess(result); }
         },
         error: function (xhr, status, error) {
           if (!isTimedOut) {
             isHttpErrored = true;
             onError(error);
           }
         }
       });
   }
Creating promises with Q

   Breaking promises
   function getWithTimeout(url, ms) {
     var deferred = Q.defer();

       setTimeout(function () {
         deferred.reject(new Error("timed out"));
       }, ms);

       $.ajax(url, {
         success: deferred.resolve,
         error: deferred.reject
       });

       return deferred.promise;
   }
Creating promises with Q

   Building abstractions
   function timeout(promise, ms) {
     var deferred = Q.defer();
     promise.then(deferred.resolve, deferred.reject);

       setTimeout(function () {
         deferred.reject(new Error("timed out"));
       }, ms);

       return deferred.promise;
   }

   function getWithTimeout(url, ms) {
     return timeout(getAsPromise(url), ms);
   }
Promises are cool.
They clean up our method signatures.


They’re composable, they’re joinable,
    and they’re dependably async.


They unify various callback conventions
 into something very much like return
        values and exceptions.
But… we still have to write in CPS.
COROUTINES
“Coroutines are computer program
            components that generalize subroutines
                 to allow multiple entry points for
             suspending and resuming execution at
                             certain locations.”



http://en.wikipedia.org/wiki/Coroutine
WTF!?
Nice:
var xP = getX();
var yP = getY();
var zP = getZ();

Q.all([xP, yP, zP]).spread(function (x, y, z) {
  console.log(x + y + z);
});
Nicer:
var [x, y, z] = await Q.all([getX(), getY(), getZ()]);

console.log(x + y + z);
Nice:
$("#button").clickPromise()
  .then(promptUserForTwitterHandle)
  .then(twitter.getTweetsFor)
  .then(ui.show);
Nicer:
await $("#button").clickPromise();

var handle = await promptUserForTwitterHandle();
var tweets = await twitter.getTweetsFor(handle);

ui.show(tweets);
Q: Can’t the compiler do this for me?


A: yes… if you are willing to introduce a compiler.
Several options, none perfect
•   Kaffeine: http://weepy.github.com/kaffeine/
•   Traceur: http://tinyurl.com/traceur-js
•   TameJS: http://tamejs.org/
•   Node fork: http://tinyurl.com/node-async
Q: OK well… can’t the interpreter do this for me?


A: yes… if you’re willing to wait for the next version of JS.
The next version of JavaScript (“ECMAScript
Harmony”) has a limited form of coroutines that can
  be twisted to do something like what we want.
ECMAScript Harmony generators
 function* fibonacci() {
   var [prev, curr] = [0, 1];
   for (;;) {
     [prev, curr] = [curr, prev + curr];
      yield curr;
   }
 }

 for (n of fibonnaci()) {
   console.log(n);
 }



http://wiki.ecmascript.org/doku.php?id=harmony:generators
ECMAScript Harmony generators
 var eventualAdd = Q.async(function* (pA, pB) {
   var a = yield pA;
   var b = yield pB;
   return a + b;
 });




https://github.com/kriskowal/q/tree/master/examples/async-generators
ECMAScript Harmony generators
 // Can only use yield as we want to within
 // Q.async'ed generator functions

 Q.async(function* () {
   // Talk to the server to get one and two.
   var three = yield eventualAdd(getOne(), getTwo());

   assert.equal(three, 3);
 })();




https://groups.google.com/d/topic/q-continuum/7PWKbgeFA48/discussion
ECMAScript Harmony generators
 // Given promise-returning delay(ms) as before:

 var animateAsync = Q.async(function* (el) {
   for (var i = 0; i < 100; ++i) {
     element.style.left = i;
     yield delay(20);
   }
 });




http://wiki.ecmascript.org/doku.php?id=strawman:async_functions
ECMAScript Harmony generators
 Q.async(function* () {
   var el = document.getElementById("my-element");

    yield animateAsync(el);

   console.log("it's done animating");
 })();




https://groups.google.com/d/topic/q-continuum/7PWKbgeFA48/discussion
So coroutines are a bit of a mess, but
   we’ll see how things shape up.
Recap
•   Async is here to stay
•   But you don’t have to dive into callback hell
•   Use promises
•   Use Q
•   Maybe use coroutines if you’re feeling brave
Thanks for listening!

More Related Content

PDF
NodeJS
Predhin Sapru
 
PDF
Node JS Crash Course
Haim Michael
 
PDF
Nodejs vatsal shah
Vatsal N Shah
 
PDF
MERN Stack Roadmap for Beginner PDF By ScholarHat
Scholarhat
 
PPTX
Node js Introduction
sanskriti agarwal
 
PDF
React Js Simplified
Sunil Yadav
 
PPTX
Reactjs
Mallikarjuna G D
 
PDF
ReactJS Tutorial For Beginners | ReactJS Redux Training For Beginners | React...
Edureka!
 
Node JS Crash Course
Haim Michael
 
Nodejs vatsal shah
Vatsal N Shah
 
MERN Stack Roadmap for Beginner PDF By ScholarHat
Scholarhat
 
Node js Introduction
sanskriti agarwal
 
React Js Simplified
Sunil Yadav
 
ReactJS Tutorial For Beginners | ReactJS Redux Training For Beginners | React...
Edureka!
 

What's hot (20)

PDF
Understanding Asynchronous JavaScript
jnewmanux
 
PPTX
Promises, Promises
Domenic Denicola
 
PDF
Domain Driven Design with the F# type System -- NDC London 2013
Scott Wlaschin
 
PPTX
Python/Flask Presentation
Parag Mujumdar
 
PPTX
jQuery
Dileep Mishra
 
PPTX
Flask – Python
Max Claus Nunes
 
PPTX
Clean Code I - Best Practices
Theo Jungeblut
 
PDF
Javascript Design Patterns
Subramanyan Murali
 
PPTX
Clean Pragmatic Architecture - Avoiding a Monolith
Victor Rentea
 
PDF
Asynchronous JavaScript Programming with Callbacks & Promises
Hùng Nguyễn Huy
 
PPTX
Clean Code
Dmytro Turskyi
 
PPTX
Redux training
dasersoft
 
PPTX
Javascript 101
Shlomi Komemi
 
PDF
JavaScript - Chapter 4 - Types and Statements
WebStackAcademy
 
PDF
JavaScript Promises
Derek Willian Stavis
 
PDF
Railway Oriented Programming
Scott Wlaschin
 
PDF
WebAssembly Overview
Alexandr Skachkov
 
PDF
JavaScript - Chapter 10 - Strings and Arrays
WebStackAcademy
 
PPTX
React workshop
Imran Sayed
 
PDF
gRPC: The Story of Microservices at Square
Apigee | Google Cloud
 
Understanding Asynchronous JavaScript
jnewmanux
 
Promises, Promises
Domenic Denicola
 
Domain Driven Design with the F# type System -- NDC London 2013
Scott Wlaschin
 
Python/Flask Presentation
Parag Mujumdar
 
Flask – Python
Max Claus Nunes
 
Clean Code I - Best Practices
Theo Jungeblut
 
Javascript Design Patterns
Subramanyan Murali
 
Clean Pragmatic Architecture - Avoiding a Monolith
Victor Rentea
 
Asynchronous JavaScript Programming with Callbacks & Promises
Hùng Nguyễn Huy
 
Clean Code
Dmytro Turskyi
 
Redux training
dasersoft
 
Javascript 101
Shlomi Komemi
 
JavaScript - Chapter 4 - Types and Statements
WebStackAcademy
 
JavaScript Promises
Derek Willian Stavis
 
Railway Oriented Programming
Scott Wlaschin
 
WebAssembly Overview
Alexandr Skachkov
 
JavaScript - Chapter 10 - Strings and Arrays
WebStackAcademy
 
React workshop
Imran Sayed
 
gRPC: The Story of Microservices at Square
Apigee | Google Cloud
 
Ad

Viewers also liked (9)

PPTX
W3C Automotive 표준 개발 현황
Wonsuk Lee
 
PPTX
[하코사세미나]미리보는 대규모 자바스크립트 어플리케이션 개발
정석 양
 
PPTX
[하코사세미나] 한 시간 만에 배우는 Jquery
정석 양
 
PPTX
[ 하코사세미나] 의외로 쉬운 D3 그래프 퍼블리싱
정석 양
 
PDF
CSS 실무테크닉
Eun Cho
 
PDF
CSS 셀렉터
Eun Cho
 
PDF
[Hello world 오픈세미나]실시간웹을위한comet과socket.io
NAVER D2
 
PDF
[Hello world 오픈 세미나]ffmpeg android
NAVER D2
 
PDF
[Hello world 오픈 세미나]oauth
NAVER D2
 
W3C Automotive 표준 개발 현황
Wonsuk Lee
 
[하코사세미나]미리보는 대규모 자바스크립트 어플리케이션 개발
정석 양
 
[하코사세미나] 한 시간 만에 배우는 Jquery
정석 양
 
[ 하코사세미나] 의외로 쉬운 D3 그래프 퍼블리싱
정석 양
 
CSS 실무테크닉
Eun Cho
 
CSS 셀렉터
Eun Cho
 
[Hello world 오픈세미나]실시간웹을위한comet과socket.io
NAVER D2
 
[Hello world 오픈 세미나]ffmpeg android
NAVER D2
 
[Hello world 오픈 세미나]oauth
NAVER D2
 
Ad

Similar to Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patterns in JavaScript (20)

PDF
Event driven javascript
Francesca1980
 
PDF
Event driven javascript
Francesca1980
 
PDF
Douglas Crockford: Serversideness
WebExpo
 
PDF
Testing web APIs
FDConf
 
PDF
Event Driven Javascript
Federico Galassi
 
PPT
You promise?
IT Weekend
 
PPTX
Avoiding Callback Hell with Async.js
cacois
 
PDF
The Evolution of Async-Programming (SD 2.0, JavaScript)
jeffz
 
PDF
How to actually use promises - Jakob Mattsson, FishBrain
Codemotion Tel Aviv
 
PPTX
Avoiding callback hell in Node js using promises
Ankit Agarwal
 
ODP
Node js
hazzaz
 
PDF
Promises are so passé - Tim Perry - Codemotion Milan 2016
Codemotion
 
PDF
Async js - Nemetschek Presentaion @ HackBulgaria
HackBulgaria
 
PDF
Kamil witecki asynchronous, yet readable, code
Kamil Witecki
 
PDF
The art of concurrent programming
Iskren Chernev
 
PDF
Angular promises and http
Alexe Bogdan
 
PDF
The evolution of asynchronous JavaScript
Alessandro Cinelli (cirpo)
 
PDF
Think Async: Asynchronous Patterns in NodeJS
Adam L Barrett
 
PPTX
Async discussion 9_29_15
Cheryl Yaeger
 
PPTX
All you need to know about the JavaScript event loop
Saša Tatar
 
Event driven javascript
Francesca1980
 
Event driven javascript
Francesca1980
 
Douglas Crockford: Serversideness
WebExpo
 
Testing web APIs
FDConf
 
Event Driven Javascript
Federico Galassi
 
You promise?
IT Weekend
 
Avoiding Callback Hell with Async.js
cacois
 
The Evolution of Async-Programming (SD 2.0, JavaScript)
jeffz
 
How to actually use promises - Jakob Mattsson, FishBrain
Codemotion Tel Aviv
 
Avoiding callback hell in Node js using promises
Ankit Agarwal
 
Node js
hazzaz
 
Promises are so passé - Tim Perry - Codemotion Milan 2016
Codemotion
 
Async js - Nemetschek Presentaion @ HackBulgaria
HackBulgaria
 
Kamil witecki asynchronous, yet readable, code
Kamil Witecki
 
The art of concurrent programming
Iskren Chernev
 
Angular promises and http
Alexe Bogdan
 
The evolution of asynchronous JavaScript
Alessandro Cinelli (cirpo)
 
Think Async: Asynchronous Patterns in NodeJS
Adam L Barrett
 
Async discussion 9_29_15
Cheryl Yaeger
 
All you need to know about the JavaScript event loop
Saša Tatar
 

More from Domenic Denicola (20)

PPTX
The State of JavaScript (2015)
Domenic Denicola
 
PPTX
Async Frontiers
Domenic Denicola
 
PPTX
The jsdom
Domenic Denicola
 
PPTX
The Final Frontier
Domenic Denicola
 
PPTX
ES6 in Real Life
Domenic Denicola
 
PPTX
Streams for the Web
Domenic Denicola
 
PPTX
After Return of the Jedi
Domenic Denicola
 
PPTX
The State of JavaScript
Domenic Denicola
 
PPTX
How to Win Friends and Influence Standards Bodies
Domenic Denicola
 
PPTX
The Extensible Web
Domenic Denicola
 
PPTX
The Promised Land (in Angular)
Domenic Denicola
 
PDF
ES6: The Awesome Parts
Domenic Denicola
 
PDF
Boom! Promises/A+ Was Born
Domenic Denicola
 
PPTX
Domains!
Domenic Denicola
 
PPTX
Client-Side Packages
Domenic Denicola
 
PPTX
Creating Truly RESTful APIs
Domenic Denicola
 
PPTX
JavaScript on the Desktop
Domenic Denicola
 
PPTX
ES6 is Nigh
Domenic Denicola
 
PPTX
Real World Windows 8 Apps in JavaScript
Domenic Denicola
 
PDF
Unit Testing for Great Justice
Domenic Denicola
 
The State of JavaScript (2015)
Domenic Denicola
 
Async Frontiers
Domenic Denicola
 
The jsdom
Domenic Denicola
 
The Final Frontier
Domenic Denicola
 
ES6 in Real Life
Domenic Denicola
 
Streams for the Web
Domenic Denicola
 
After Return of the Jedi
Domenic Denicola
 
The State of JavaScript
Domenic Denicola
 
How to Win Friends and Influence Standards Bodies
Domenic Denicola
 
The Extensible Web
Domenic Denicola
 
The Promised Land (in Angular)
Domenic Denicola
 
ES6: The Awesome Parts
Domenic Denicola
 
Boom! Promises/A+ Was Born
Domenic Denicola
 
Client-Side Packages
Domenic Denicola
 
Creating Truly RESTful APIs
Domenic Denicola
 
JavaScript on the Desktop
Domenic Denicola
 
ES6 is Nigh
Domenic Denicola
 
Real World Windows 8 Apps in JavaScript
Domenic Denicola
 
Unit Testing for Great Justice
Domenic Denicola
 

Recently uploaded (20)

PDF
Orbitly Pitch Deck|A Mission-Driven Platform for Side Project Collaboration (...
zz41354899
 
PDF
Oracle AI Vector Search- Getting Started and what's new in 2025- AIOUG Yatra ...
Sandesh Rao
 
PDF
MASTERDECK GRAPHSUMMIT SYDNEY (Public).pdf
Neo4j
 
PPTX
The-Ethical-Hackers-Imperative-Safeguarding-the-Digital-Frontier.pptx
sujalchauhan1305
 
PDF
Economic Impact of Data Centres to the Malaysian Economy
flintglobalapac
 
PDF
REPORT: Heating appliances market in Poland 2024
SPIUG
 
PDF
Security features in Dell, HP, and Lenovo PC systems: A research-based compar...
Principled Technologies
 
PDF
AI-Cloud-Business-Management-Platforms-The-Key-to-Efficiency-Growth.pdf
Artjoker Software Development Company
 
PPTX
What-is-the-World-Wide-Web -- Introduction
tonifi9488
 
PPTX
IT Runs Better with ThousandEyes AI-driven Assurance
ThousandEyes
 
PDF
A Strategic Analysis of the MVNO Wave in Emerging Markets.pdf
IPLOOK Networks
 
PDF
A Day in the Life of Location Data - Turning Where into How.pdf
Precisely
 
PPTX
cloud computing vai.pptx for the project
vaibhavdobariyal79
 
PDF
Accelerating Oracle Database 23ai Troubleshooting with Oracle AHF Fleet Insig...
Sandesh Rao
 
PDF
Presentation about Hardware and Software in Computer
snehamodhawadiya
 
PDF
Using Anchore and DefectDojo to Stand Up Your DevSecOps Function
Anchore
 
PDF
How Open Source Changed My Career by abdelrahman ismail
a0m0rajab1
 
PDF
Responsible AI and AI Ethics - By Sylvester Ebhonu
Sylvester Ebhonu
 
PPTX
Applied-Statistics-Mastering-Data-Driven-Decisions.pptx
parmaryashparmaryash
 
PDF
CIFDAQ's Market Wrap : Bears Back in Control?
CIFDAQ
 
Orbitly Pitch Deck|A Mission-Driven Platform for Side Project Collaboration (...
zz41354899
 
Oracle AI Vector Search- Getting Started and what's new in 2025- AIOUG Yatra ...
Sandesh Rao
 
MASTERDECK GRAPHSUMMIT SYDNEY (Public).pdf
Neo4j
 
The-Ethical-Hackers-Imperative-Safeguarding-the-Digital-Frontier.pptx
sujalchauhan1305
 
Economic Impact of Data Centres to the Malaysian Economy
flintglobalapac
 
REPORT: Heating appliances market in Poland 2024
SPIUG
 
Security features in Dell, HP, and Lenovo PC systems: A research-based compar...
Principled Technologies
 
AI-Cloud-Business-Management-Platforms-The-Key-to-Efficiency-Growth.pdf
Artjoker Software Development Company
 
What-is-the-World-Wide-Web -- Introduction
tonifi9488
 
IT Runs Better with ThousandEyes AI-driven Assurance
ThousandEyes
 
A Strategic Analysis of the MVNO Wave in Emerging Markets.pdf
IPLOOK Networks
 
A Day in the Life of Location Data - Turning Where into How.pdf
Precisely
 
cloud computing vai.pptx for the project
vaibhavdobariyal79
 
Accelerating Oracle Database 23ai Troubleshooting with Oracle AHF Fleet Insig...
Sandesh Rao
 
Presentation about Hardware and Software in Computer
snehamodhawadiya
 
Using Anchore and DefectDojo to Stand Up Your DevSecOps Function
Anchore
 
How Open Source Changed My Career by abdelrahman ismail
a0m0rajab1
 
Responsible AI and AI Ethics - By Sylvester Ebhonu
Sylvester Ebhonu
 
Applied-Statistics-Mastering-Data-Driven-Decisions.pptx
parmaryashparmaryash
 
CIFDAQ's Market Wrap : Bears Back in Control?
CIFDAQ
 

Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patterns in JavaScript

  • 1. Callbacks, Promises, and Coroutines (oh my!) Asynchronous Programming Patterns in JavaScript Domenic Denicola http://domenicdenicola.com @domenicdenicola
  • 2. In non-web languages, most of the code we write is synchronous. aka blocking
  • 3. Console.WriteLine("What is your name?"); string name = Console.ReadLine(); Console.WriteLine("Hello, " + name);
  • 4. var fileNames = Directory.EnumerateFiles("C:"); foreach (var fileName in fileNames) { using (var f = File.Open(fileName, FileMode.Open)) { Console.WriteLine(fileName + " " + f.Length); } }
  • 5. using (var client = new WebClient()) { string html = client.DownloadString("http://news.ycombinator.com"); Console.WriteLine(html.Contains("Google")); Console.WriteLine(html.Contains("Microsoft")); Console.WriteLine(html.Contains("Apple")); }
  • 6. Thread.Start BackgroundWorker Control.InvokeRequired This often causes us some pain… … but hey, there’s always threads! Dispatcher.Invoke ThreadPool .AsParallel()
  • 7. Q: What are these threads doing, most of the time? A: waiting
  • 9. In JavaScript, we do things differently.
  • 10. There’s only one thread in JavaScript, so we use that thread to get stuff done.
  • 11. OK, let’s talk about… • The event loop • Callbacks • Promises • Coroutines
  • 13. You’ve seen event loops before:
  • 14. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MSG msg; while (GetMessage(&msg, NULL, 0, 0) > 0) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }
  • 15. this.btnOK.Click += this.btnOK_Click; private void btnOK_Click(object sender, EventArgs e) { // ... }
  • 16. $("#ok-button").click(function () { // ... }); setTimeout(function () { // ... }, 100); $.get("http://example.com", function (result) { // ... });
  • 17. Some event loop subtleties • Yielding • Async’s not sync • Errors • It’s not magic
  • 18. Event Loop Subtleties Yielding console.log("1"); $.get("/echo/2", function (result) { console.log(result); }); console.log("3"); // 1, 3, 2
  • 19. Event Loop Subtleties Async’s not sync var hi = null; $.get("/echo/hi", function (result) { hi = result; }); console.log(hi); // null
  • 20. Event Loop Subtleties Errors console.log("About to get the website..."); $.ajax("http://sometimesdown.example.com", { success: function (result) { console.log(result); }, error: function () { throw new Error("Error getting the website"); } }); console.log("Continuing about my business...");
  • 21. Event Loop Subtleties It’s not magic function fib(n) { return n < 2 ? 1 : fib(n-2) + fib(n-1); } console.log("1"); setTimeout(function () { console.log("2"); }, 100); fib(40); // 1 ... 15 seconds later ... 2 http://teddziuba.com/2011/10/node-js-is-cancer.html
  • 22. The event loop is tricky… but powerful.
  • 24. What we’ve seen so far has been doing asynchronicity through callbacks.
  • 25. Callbacks are OK for simple operations, but force us into continuation passing style.
  • 26. Recurring StackOverflow question: function getY() { var y; $.get("/gety", function (jsonData) { y = jsonData.y; }); return y; } Why doesn’t it work??? var x = 5; var y = getY(); console.log(x + y);
  • 27. After getting our data, we have to do everything else in a continuation:
  • 28. function getY(continueWith) { $.get("/gety", function (jsonData) { continueWith(jsonData.y); }); } var x = 5; getY(function (y) { console.log(x + y); });
  • 29. CPS Headaches • Doing things in sequence is hard • Doing things in parallel is harder • Errors get lost easily
  • 30. CPS Headaches Doing things in sequence is hard $("#button").click(function () { promptUserForTwitterHandle(function (handle) { twitter.getTweetsFor(handle, function (tweets) { ui.show(tweets); }); }); });
  • 31. CPS Headaches Doing things in parallel is harder var tweets, answers, checkins; twitter.getTweetsFor("domenicdenicola", function (result) { tweets = result; somethingFinished(); }); stackOverflow.getAnswersFor("Domenic", function (result) { answers = result; somethingFinished(); }); fourSquare.getCheckinsBy("Domenic", function (result) { checkins = result; somethingFinished(); });
  • 32. CPS Headaches Doing things in parallel is harder var finishedSoFar = 0; function somethingFinished() { if (++finishedSoFar === 3) { ui.show(tweets, answers, checkins); } }
  • 33. CPS Headaches Errors get lost easily function getTotalFileLengths(path, callback) { fs.readdir(path, function (err, fileNames) { var total = 0; var finishedSoFar = 0; function finished() { if (++finishedSoFar === fileNames.length) { callback(total); } } fileNames.forEach(function (fileName) { fs.readFile(fileName, function (err, file) { total += file.length; finished(); }); }); }); }
  • 34. You could write your own library to make this nicer…
  • 35. function parallel(actions, callback) { var results = []; function finished(result) { results.push(result); if (results.length === actions.length) { callback(results); } } actions.forEach(function (action) { action(finished); }); }
  • 36. parallel([ function (cb) { twitter.getTweetsFor("domenicdenicola", cb); }, function (cb) { stackOverflow.getAnswersFor("Domenic", cb); }, function (cb) { fourSquare.getCheckinsFor("Domenic", cb); } ], function (results) { console.log("tweets = ", results[0]); console.log("answers = ", results[1]); console.log("checkins = ", results[2]); });
  • 37. And in fact many people have: https://github.com/joyent/node/wiki/modules#wiki-async-flow
  • 38. The best of these (IMO) are based on an abstraction called “promises”
  • 40. Un-inverts the chain of responsibility: instead of calling a passed callback, return a promise.
  • 41. addWithCallback(a, b, function (result) { assert.equal(result, a + b); }); var promise = addWithPromise(a, b); promise.then(function (result) { assert.equal(result, a + b); });
  • 42. Why promises are awesome • Cleaner method signatures • Uniform return/error semantics • Easy composition • Easy sequential/parallel join • Always async • Exception-style error bubbling
  • 43. Promises are Awesome Cleaner method signatures Uniform return/error semantics $.get( url, [data], [success(data, status, xhr)], [dataType] )
  • 44. Promises are Awesome Cleaner method signatures Uniform return/error semantics $.ajax(url, settings) settings.success(data, status, xhr) settings.error(xhr, status, errorThrown) settings.complete(xhr, status)
  • 45. Promises are Awesome Cleaner method signatures Uniform return/error semantics fs.open( path, flags, [mode], [callback(error, file)] )
  • 46. Promises are Awesome Cleaner method signatures Uniform return/error semantics fs.write( file, buffer, offset, length, position, [callback(error, written, buffer)] )
  • 47. Promises are Awesome Cleaner method signatures Uniform return/error semantics getAsPromise(url, [data], [dataType]).then( function onFulfilled(result) { var data = result.data; var status = result.status; var xhr = result.xhr; }, function onBroken(error) { console.error("Couldn't get", error); } );
  • 48. Promises are Awesome Easy composition function getUser(userName, onSuccess, onError) { $.ajax("/user?" + userName, { success: onSuccess, error: onError }); }
  • 49. Promises are Awesome Easy composition function getUser(userName) { return getAsPromise("/user?" + userName); }
  • 50. Promises are Awesome Easy composition function getFirstName(userName, onSuccess, onError) { $.ajax("/user?" + userName, { success: function successProxy(data) { onSuccess(data.firstName); }, error: onError }); }
  • 51. Promises are Awesome Easy composition function getFirstName(userName) { return getAsPromise("/user?" + userName) .get("firstName"); }
  • 52. Promises are Awesome Easy sequential join $("#button").click(function () { promptUserForTwitterHandle(function (handle) { twitter.getTweetsFor(handle, function (tweets) { ui.show(tweets); }); }); });
  • 53. Promises are Awesome Easy sequential join $("#button").clickPromise() .then(promptUserForTwitterHandle) .then(twitter.getTweetsFor) .then(ui.show);
  • 54. Promises are Awesome Easy parallel join var tweets, answers, checkins; twitter.getTweetsFor("domenicdenicola", function (result) { tweets = result; somethingFinished(); }); stackOverflow.getAnswersFor("Domenic", function (result) { answers = result; somethingFinished(); }); fourSquare.getCheckinsBy("Domenic", function (result) { checkins = result; somethingFinished(); });
  • 55. Promises are Awesome Easy parallel join Q.all([ twitter.getTweetsFor("domenicdenicola"), stackOverflow.getAnswersFor("Domenic"), fourSquare.getCheckinsBy("Domenic") ]).then(function (results) { console.log(results[0], results[1], results[2]); });
  • 56. Promises are Awesome Easy parallel join Q.all([ twitter.getTweetsFor("domenicdenicola"), stackOverflow.getAnswersFor("Domenic"), fourSquare.getCheckinsBy("Domenic") ]).spread(function (tweets, answers, checkins) { console.log(tweets, answers, checkins); });
  • 57. Promises are Awesome Always async function getUser(userName, onSuccess, onError) { if (cache.has(userName)) { onSuccess(cache.get(userName)); } else { $.ajax("/user?" + userName, { success: onSuccess, error: onError }); } }
  • 58. Promises are Awesome Always async console.log("1"); getUser("ddenicola", function (user) { console.log(user.firstName); }); console.log("2"); // 1, 2, Domenic
  • 59. Promises are Awesome Always async console.log("1"); getUser("ddenicola", function (user) { console.log(user.firstName); }); console.log("2"); // 1, Domenic, 2
  • 60. Promises are Awesome Always async function getUser(userName) { if (cache.has(userName)) { return Q.ref(cache.get(userName)); } else { return getWithPromise("/user?" + userName); } }
  • 61. Promises are Awesome Always async console.log("1"); getUser("ddenicola").then(function (user) { console.log(user.firstName); }); console.log("2"); // 1, 2, Domenic (every time)
  • 62. Promises are Awesome Exception-style error bubbling getUser("Domenic", function (user) { getBestFriend(user, function (friend) { ui.showBestFriend(friend); }); });
  • 63. Promises are Awesome Exception-style error bubbling getUser("Domenic", function (err, user) { if (err) { ui.error(err); } else { getBestFriend(user, function (err, friend) { if (err) { ui.error(err); } else { ui.showBestFriend(friend); } }); } });
  • 64. Promises are Awesome Exception-style error bubbling getUser("Domenic") .then(getBestFriend, ui.error) .then(ui.showBestFriend, ui.error);
  • 65. Promises are Awesome Exception-style error bubbling getUser("Domenic") .then(getBestFriend) .then(ui.showBestFriend, ui.error);
  • 66. Promises are Awesome Exception-style error bubbling ui.startSpinner(); getUser("Domenic") .then(getBestFriend) .then( function (friend) { ui.showBestFriend(friend); ui.stopSpinner(); }, function (error) { ui.error(error); ui.stopSpinner(); } );
  • 67. Promises are Awesome Exception-style error bubbling ui.startSpinner(); getUser("Domenic") .then(getBestFriend) .then(ui.showBestFriend, ui.error) .fin(ui.stopSpinner);
  • 68. Promises are Awesome Exception-style error bubbling function getBestFriendAndDontGiveUp(user) { return getUser(user).then( getBestFriend, function (error) { if (error instanceof TemporaryNetworkError) { console.log("Retrying after error", error); return getBestFriendAndDontGiveUp(user); } throw error; }); }
  • 69. Sounds great. How do I get in on this action?
  • 70. Use Q • By Kris Kowal, @kriskowal • https://github.com/kriskowal/q • Can consume promises from jQuery etc. • Implements various CommonJS standards
  • 71. If you’re already using jQuery’s promises, switch to Q: https://github.com/kriskowal/q/wiki/jQuery
  • 73. Creating promises with Q Fulfilling promises // We have: setTimeout(doSomething, 1000); // We want: delay(1000).then(doSomething);
  • 74. Creating promises with Q Fulfilling promises function delay(ms) { var deferred = Q.defer(); setTimeout(deferred.resolve, ms); return deferred.promise; } delay(1000).then(doSomething);
  • 75. Creating promises with Q Breaking promises function getWithTimeout(url, ms, onSuccess, onError) { var isTimedOut = false, isHttpErrored = false; setTimeout(function () { if (!isHttpErrored) { isTimedOut = true; onError(new Error("timed out")); } }, ms); $.ajax(url, { success: function (result) { if (!isTimedOut) { onSuccess(result); } }, error: function (xhr, status, error) { if (!isTimedOut) { isHttpErrored = true; onError(error); } } }); }
  • 76. Creating promises with Q Breaking promises function getWithTimeout(url, ms) { var deferred = Q.defer(); setTimeout(function () { deferred.reject(new Error("timed out")); }, ms); $.ajax(url, { success: deferred.resolve, error: deferred.reject }); return deferred.promise; }
  • 77. Creating promises with Q Building abstractions function timeout(promise, ms) { var deferred = Q.defer(); promise.then(deferred.resolve, deferred.reject); setTimeout(function () { deferred.reject(new Error("timed out")); }, ms); return deferred.promise; } function getWithTimeout(url, ms) { return timeout(getAsPromise(url), ms); }
  • 79. They clean up our method signatures. They’re composable, they’re joinable, and they’re dependably async. They unify various callback conventions into something very much like return values and exceptions.
  • 80. But… we still have to write in CPS.
  • 82. “Coroutines are computer program components that generalize subroutines to allow multiple entry points for suspending and resuming execution at certain locations.” http://en.wikipedia.org/wiki/Coroutine
  • 83. WTF!?
  • 84. Nice: var xP = getX(); var yP = getY(); var zP = getZ(); Q.all([xP, yP, zP]).spread(function (x, y, z) { console.log(x + y + z); });
  • 85. Nicer: var [x, y, z] = await Q.all([getX(), getY(), getZ()]); console.log(x + y + z);
  • 86. Nice: $("#button").clickPromise() .then(promptUserForTwitterHandle) .then(twitter.getTweetsFor) .then(ui.show);
  • 87. Nicer: await $("#button").clickPromise(); var handle = await promptUserForTwitterHandle(); var tweets = await twitter.getTweetsFor(handle); ui.show(tweets);
  • 88. Q: Can’t the compiler do this for me? A: yes… if you are willing to introduce a compiler.
  • 89. Several options, none perfect • Kaffeine: http://weepy.github.com/kaffeine/ • Traceur: http://tinyurl.com/traceur-js • TameJS: http://tamejs.org/ • Node fork: http://tinyurl.com/node-async
  • 90. Q: OK well… can’t the interpreter do this for me? A: yes… if you’re willing to wait for the next version of JS.
  • 91. The next version of JavaScript (“ECMAScript Harmony”) has a limited form of coroutines that can be twisted to do something like what we want.
  • 92. ECMAScript Harmony generators function* fibonacci() { var [prev, curr] = [0, 1]; for (;;) { [prev, curr] = [curr, prev + curr]; yield curr; } } for (n of fibonnaci()) { console.log(n); } http://wiki.ecmascript.org/doku.php?id=harmony:generators
  • 93. ECMAScript Harmony generators var eventualAdd = Q.async(function* (pA, pB) { var a = yield pA; var b = yield pB; return a + b; }); https://github.com/kriskowal/q/tree/master/examples/async-generators
  • 94. ECMAScript Harmony generators // Can only use yield as we want to within // Q.async'ed generator functions Q.async(function* () { // Talk to the server to get one and two. var three = yield eventualAdd(getOne(), getTwo()); assert.equal(three, 3); })(); https://groups.google.com/d/topic/q-continuum/7PWKbgeFA48/discussion
  • 95. ECMAScript Harmony generators // Given promise-returning delay(ms) as before: var animateAsync = Q.async(function* (el) { for (var i = 0; i < 100; ++i) { element.style.left = i; yield delay(20); } }); http://wiki.ecmascript.org/doku.php?id=strawman:async_functions
  • 96. ECMAScript Harmony generators Q.async(function* () { var el = document.getElementById("my-element"); yield animateAsync(el); console.log("it's done animating"); })(); https://groups.google.com/d/topic/q-continuum/7PWKbgeFA48/discussion
  • 97. So coroutines are a bit of a mess, but we’ll see how things shape up.
  • 98. Recap • Async is here to stay • But you don’t have to dive into callback hell • Use promises • Use Q • Maybe use coroutines if you’re feeling brave

Editor's Notes

  • #42: Decouples the action from the subsequent use of the result.