Slide 1

Slide 1 text

REDEMPTION FROM CALLBACK HELL

Slide 2

Slide 2 text

ASYNCHRONOUS LET’S TALK ABOUT JAVASCRIPT APIs

Slide 3

Slide 3 text

function getUser(name) { var sql = 'SELECT * FROM users WHERE name=?'; var user = query(sql, name); // <- blocking if (!user) throw new Error('no user!'); return user; }

Slide 4

Slide 4 text

function getUser(name, callback) { var sql = 'SELECT * FROM users WHERE name=?'; query(sql, name, function (error, user) { if (error) { callback(error); } else if (!user) { callback(new Error('no user!')); } else { callback(null, user); } }); }

Slide 5

Slide 5 text

function getUser(name) { var sql = 'SELECT * FROM users WHERE name=?'; var user = query(sql, name); // <- blocking if (!user) throw new Error('no user!'); return user; } function getUser(name, callback) { var sql = 'SELECT * FROM users WHERE name=?'; query(sql, name, function (error, user) { if (error) { callback(error); } else if (!user) { callback(new Error('no user!')); } else { callback(null, user); } }); }

Slide 6

Slide 6 text

NO RETURN IN A CALLBACK, THERE IS

Slide 7

Slide 7 text

function getUser(name, callback) { var sql = 'SELECT * FROM users WHERE name=?'; query(sql, name, function (error, user) { // Nobody gets this return value! return user; }); }

Slide 8

Slide 8 text

NO THROW IN A CALLBACK, THERE IS

Slide 9

Slide 9 text

function getUser(name, callback) { var sql = 'SELECT * FROM users WHERE name=?'; query(sql, name, function (error, user) { // Nobody can catch this error! if (error) throw error; }); }

Slide 10

Slide 10 text

function getUser(name, callback) { var sql = 'SELECT * FROM users WHERE name=?'; query(sql, name, function (error, user) { // Nobody can catch this error! if (error) throw error; // <- BAD }); }

Slide 11

Slide 11 text

• NO RETURN • NO THROW

Slide 12

Slide 12 text

function getUser(name) { var sql = 'SELECT * FROM users WHERE name=?'; var user = query(sql, name); // <- blocking if (!user) throw new Error('no user!'); return user; } function getUser(name, callback) { var sql = 'SELECT * FROM users WHERE name=?'; query(sql, name, function (error, user) { if (error) { callback(error); } else if (!user) { callback(new Error('no user!')); } else { callback(null, user); } }); }

Slide 13

Slide 13 text

NO STACK IN A CALLBACK, THERE IS

Slide 14

Slide 14 text

NO GUARANTEES USING CALLBACKS, THERE ARE

Slide 15

Slide 15 text

function getUsers(names, callback) { var sql = 'SELECT * FROM users WHERE name=?'; var users = []; names.forEach(function (name) { query(sql, name, function (error, user) { if (error) { callback(error); } else if (!user) { callback(new Error('no user!')); } else { users.push(user); if (users.length === names.length) { callback(null, users); } } }); }); }

Slide 16

Slide 16 text

• NO STACK (NO RETURN/THROW) • NO GUARANTEES • BUT AT LEAST WE DON’T BLOCK!?

Slide 17

Slide 17 text

function getUser(name) { var sql = 'SELECT * FROM users WHERE name=?'; var user = query(sql, name); // <- blocking if (!user) throw new Error('no user!'); return user; } function getUser(name, callback) { var sql = 'SELECT * FROM users WHERE name=?'; query(sql, name, function (error, user) { if (error) { callback(error); } else if (!user) { callback(new Error('no user!')); } else { callback(null, user); } }); }

Slide 18

Slide 18 text

ES6 GENERATORS THE FUTURE IS

Slide 19

Slide 19 text

function fibonacci() { var i = 0, j = 1; while (true) { yield i; var t = i; i = j; j += t; } } var generator = fibonacci(); for (var i = 0; i < 12; i++) { print(generator.next()); }

Slide 20

Slide 20 text

function fibonacci() { var i = 0, j = 1; while (true) { yield i; // <- awesome var t = i; i = j; j += t; } } var generator = fibonacci(); for (var i = 0; i < 12; i++) { print(generator.next()); }

Slide 21

Slide 21 text

• FIRST-CLASS COROUTINES • SUSPEND EXECUTION CONTEXT • SMALL API • NOT AVAILABLE YET

Slide 22

Slide 22 text

PROMISES/A+

Slide 23

Slide 23 text

• DISCOVERED CIRCA 1989 • INSPIRED BY E • WIDELY USED OUTSIDE JS

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

Horrible Lies!!!

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

https://github.com/promises-aplus/promises-spec

Slide 31

Slide 31 text

• Q • RSVP • when https://github.com/promises-aplus/promises-spec/blob/master/implementations.md

Slide 32

Slide 32 text

ASYNCHRONOUS A PROMISE IS AN VALUE

Slide 33

Slide 33 text

getUser('mjackson', function (error, user) { // ... }); // becomes getUser('mjackson').then(function (user) { // ... }, function (error) { // ... });

Slide 34

Slide 34 text

TRANSFORMS THERE ARE 4 BASIC SYNC TO ASYNC

Slide 35

Slide 35 text

var user = getUser('mjackson'); var name = user.name; // becomes getUser('mjackson').then(function (user) { return user.name; });

Slide 36

Slide 36 text

var user = getUser('mjackson'); if (!user) throw new Error('no user!'); var name = user.name; // becomes getUser('mjackson').then(function (user) { if (!timeline) throw new Error('no user!'); return user.name; });

Slide 37

Slide 37 text

try { deliverTweetTo(tweet, 'mjackson'); } catch (error) { handleError(error); } // becomes deliverTweetTo(tweet, 'mjackson') .then(undefined, handleError);

Slide 38

Slide 38 text

try { var user = getUser('mjackson'); } catch (error) { throw new Error('ERROR: ' + error.message); } // becomes getUser('mjackson').then(undefined, function (error) { throw new Error('ERROR: ' + error.message); });

Slide 39

Slide 39 text

try { var user = getUser('mjackson'); } catch (error) { throw new Error('ERROR: ' + error.message); } // becomes getUser('mjackson').then(undefined, function (error) { throw new Error('ERROR: ' + error.message); });

Slide 40

Slide 40 text

•STACK SEMANTICS •GUARANTEES

Slide 41

Slide 41 text

SEQUENCE OPERATIONS IN

Slide 42

Slide 42 text

var user = getUser('mjackson'); var tweets = getNewTweets(user); updateTimeline(tweets); // using callbacks getUser('mjackson', function (user) { getNewTweets(user, function (tweets) { updateTimeline(tweets); }); }); // using promises getUser('mjackson') .then(getNewTweets) .then(updateTimeline);

Slide 43

Slide 43 text

EXCEPTIONS HANDLING

Slide 44

Slide 44 text

try { var user = getUser('mjackson'); var tweets = getNewTweets(user); updateTimeline(tweets); } catch (error) { handleError(error); }

Slide 45

Slide 45 text

getUser('mjackson', function (error, user) { if (error) { handleError(error); } else { getNewTweets(user, function (error, tweets) { if (error) { handleError(error); } else { updateTimeline(tweets, function (error) { if (error) handleError(error); }); } }); } });

Slide 46

Slide 46 text

// Same example, complete with node.js time bombs! getUser('mjackson', function (error, user) { if (error) throw error; getNewTweets(user, function (error, tweets) { if (error) throw error; updateTimeline(tweets, function (error) { if (error) throw error; }); }); }); process.on('uncaughtException', handleError); // LOL

Slide 47

Slide 47 text

try { var user = getUser('mjackson'); var tweets = getNewTweets(user); updateTimeline(tweets); } catch (error) { handleError(error); } // becomes getUser('mjackson') .then(getNewTweets) .then(updateTimeline) .then(undefined, handleError);

Slide 48

Slide 48 text

FUD DISPEL THE

Slide 49

Slide 49 text

var fs = require('fs'); var q = require('q'); var readFile = q.denodeify(fs.readFile); var promise = readFile(__filename, 'utf8'); promise.then(console.log, console.error);

Slide 50

Slide 50 text

function createUser(userName, userData, callback) { return database.ensureUserNameNotTaken(userName) .then(function () { database.saveUserData(userName, userData); }) .nodeify(callback); }

Slide 51

Slide 51 text

•PROMISES COMING TO THE DOM •GENERATORS COMING TO V8 •PROMISES ARE FUTURE-PROOF

Slide 52

Slide 52 text

var q = require('q'); var getUser = q.async(function * (name) { var sql = 'SELECT * FROM users WHERE name=?'; var user = yield query(sql, name); if (!user) throw new Error('no user!'); return user; });

Slide 53

Slide 53 text

PARALLEL EXECUTE OPERATIONS IN

Slide 54

Slide 54 text

function getNewTweetsForUsers(users) { return users.map(getNewTweets); } // becomes var q = require('q'); function getNewTweetsForUsers(users) { var promises = users.map(getNewTweets); return q.all(promises); }

Slide 55

Slide 55 text

SERVERS BUILD FAULT-TOLERANT

Slide 56

Slide 56 text

var http = require('http'); var q = require('q'); var server = http.createServer(function (req, res) { handleRequest(req).then(function (response) { res.writeHead(response.status, response.headers); res.end(response.content); }, function (error) { console.error(error); res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end('Internal Server Error'); }); }); server.listen(3000);

Slide 57

Slide 57 text

https://github.com/machjs/mach

Slide 58

Slide 58 text

THANKS!