只接受回调函数作为参数的单参数函数叫做 Thunk 函数。
Thunk 函数可以用于 Generator 函数的自动流程管理。使Generator 函数可以自动执行

任何函数,只要参数有回调函数,就能写成 Thunk 函数的形式。
下面是一个将多参函数转换成thunk函数的转换器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ES5版本
var Thunk = function(fn){
return function (){
var args = Array.prototype.slice.call(arguments);
return function (callback){
args.push(callback);
return fn.apply(this, args);
}
};
};
// ES6版本
var Thunk = function(fn) {
return function (...args) {
return function (callback) {
return fn.call(this, ...args, callback);
}
};
};

生产环境的转换器,建议使用 Thunkify 模块。
npm install thunkify

thunk配合Generator进行异步操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//Generator 函数的自动执行器
function run(fn) {
var gen = fn();
function next(data) {
var result = gen.next(data);
if (result.done){
console.log("done");
return;
};
result.value(next);
}
next();
}
//request函数模拟异步操作
function request(data,cb) {
setTimeout(function(){
cb(data+1);
},1000);
}
//Generator函数
function* g() {
console.log("start");
var a = yield Thunk(request)(1);
console.log(a);
var b = yield Thunk(request)(2);
console.log(b);
var c = yield Thunk(request)(3);
console.log(c);
console.log("end");
}
run(g);//执行Generator函数

最终的输出结果是
start
2
3
4
end
done
2、3、4间隔1s输出

首先通过Thunk方法将request方法转换成Thunk函数,上面代码的run函数,是一个 Generator 函数的自动执行器。内部的next函数就是 Thunk 的回调函数(最终就是request中的cb参数)。next函数先将指针移到 Generator 函数的下一步(gen.next方法),然后判断 Generator 函数是否结束(result.done属性),如果没结束,就将next函数再传入 Thunk 函数(result.value属性),否则就直接退出。

有了这个执行器,执行 Generator 函数方便多了。不管内部有多少个异步操作,直接把 Generator 函数传入run函数即可。当然,前提是每一个异步操作,都要是 Thunk 函数,也就是说,跟在yield命令后面的必须是 Thunk 函数。

所以上面的request可以改写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function request(data){
return function(cb){
setTimeout(function(){
cb(data+1);
},1000);
}
}
function* g() {
console.log("start");
var a = yield request(1);
console.log(a);
var b = yield request(2);
console.log(b);
var c = yield request(3);
console.log(c);
console.log("end");
}

就不用Thunk进行转换了