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
A helper subroutine, called thunk
, that calculates the value of the argument
Let's think of it as 'temporary function expression'
'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 withoutGenerator
"
But let's take a lookPromise
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 withgenerator
+ 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
allowsthunk
andPromise
, that's why we introduce Thunk earily this slides
async
is syntactic sugar for generator
, the usage is similar. Few advantages:
async
and await
are more clear than *
and yield
await
allows more types
Let's refactor a bit with previous generator
version
'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