On Github leftstick / javascript-async-history
Created by Howard.Zuo / @leftstick
getSession(function(session){ console.log('userId is', session.userId);//handle async process easily });
getSession(function(session){ getCurrentUser(session.userId, function(user){ getUserInfo(user.bestFriendId, function(friend){ getPosts(friend.id, function(posts){ console.log('posts are', posts);//tricky? }); }); }); });We will use this example in the whole slides, so keep it in mind would be good^^
Many years ago, while computer scientists were trying to figure out which the best evaluation strategies is, two options are available
'use strict' var x = 1; var sum = function(m){ return m + 5; }; //call by value means following two calls are the same sum(x + 2); sum(3);Normally, we believe this cost a bit more, since the expression gets evaluated before it really used
'use strict' var x = 1; var sum = function(m){ return m + 5; }; //call by name means sum(x + 2); //is the same as following (x + 2) + 5;It might produce multiple versions of the subroutine and multiple copies of the expression code by simply substitute the code of an argument expression for each appearance of the corresponding parameter in the subroutine
'use strict' var x = 1; var sum = function(exp){ return exp() + 5; }; //extract the expression into a function //pass the function as argument into sum var thunky = function () { return x + 2; }; sum(thunky);What does this have to do with async? hold your horses, let's move on
Most async JavaScript APIs accept arguments, and end up with an optional callback
'use strict' var fs = require('fs'); fs.readFile('/etc/hosts', 'utf-8', function(err, data){ console.log('the content in hosts is', data); });Image what can we do with Thunk?
'use strict' var fs = require('fs'); //we call this function as "thunkify" var readFile = function(fileName, options){ return function(callback){ return fs.readFile(fileName, options, callback); }; }; var thunky = readFile('/etc/hosts', 'utf-8'); //accept callback as single argument thunky(callback);You may say "this makes my code even more complex" Don't worry, tj had written a cool library for you, It is thunkify, let's refactor a bit with this thunkify
'use strict' var thunkify = require('thunkify'); var fs = require('fs'); var readFile = thunkify(fs.readFile); var thunky = readFile('/etc/hosts', 'utf-8'); thunky(callback);You may ask: "How does this help my async code?" I would say: "Nothing useful without Generator" But let's take a look Promise first?
'use strict' getSession() .then(function(session){ return getCurrentUser(session.userId); }) .then(function(user){ return getUserInfo(user.bestFriendId); }) .then(function(friend){ return getPosts(friend.id); }) .then(function(posts){ console.log('posts are', posts); }) .catch(function(err){ console.error('ERROR', err); });This is better than callback hell, right? Once you'd like to know how Promise achieved this, here is the place
'use strict' var fs = require('fs'); //most node APIs are callback-based //But we love something like this fs.readFile('/etc/hosts', 'utf-8') .then(function(data){ console.log('the content in hosts is', data); }) .catch(function(err){ console.error('ERROR', err); });
Let me introduce Promiseify
Let's see how it helps on above code
'use strict' var fs = require('fs'); var promiseify = require('just-promiseify'); var readFile = promiseify(fs.readFile);//usage just like thunkify readFile('/etc/hosts', 'utf-8') .then(function(data){ console.log('the content in hosts is', data); }) .catch(function(err){ console.error('ERROR', err); });Turn a regular callback-based function into one which returns a Promise
No actually, generator improves our code even further
'use strict' try{ var session = getSession(); var user = getCurrentUser(session.userId); var friend = getUserInfo(user.bestFriendId); var posts = getPosts(friend.id); console.log('posts are', posts); }catch(e){ console.error('ERROR', err); }It's not a dream with generator + co Let's refactor a bit
'use strict' var co = require('co'); var execute = function*() {//this is a generator try {//the thing right after yield, we call them "yieldables" var session = yield getSession(); var user = yield getCurrentUser(session.userId); var friend = yield getUserInfo(user.bestFriendId); var posts = yield getPosts(friend.id); console.log('posts are', posts); } catch (e) { console.error('ERROR', err); } }; co(execute);yield allows thunk and Promise, that's why we introduce Thunk earily this slides
async is syntactic sugar for generator, the usage is similar. Few advantages:
'use strict' var execute = async function() {//this is async function try {//the thing right after yield, we call them "yieldables" var session = await getSession(); var user = await getCurrentUser(session.userId); var friend = await getUserInfo(user.bestFriendId); var posts = await getPosts(friend.id); console.log('posts are', posts); } catch (e) { console.error('ERROR', err); } }; execute();
- Thunkify - Promiseify - co - Babel