Thunck函数的含义
编译器的传名调用实现,往往是将参数放到一个临时函数之中,再将这个临时函数传入函数体。这个临时函数就叫做Thunk函数。
function f(m){
return m*2;
}
f(x+5);
//等同于
var thunk = function(){
return x+5;
};
function f(thunk){
return thunk()*2;
}
上面代码中,函数f的参数x+5被一个函数替换了。凡是用到原参数的地方,对Thunk函数求值即可。
这就是thunk函数的定义,它是传名调用的一种实现策略,用来替换某个表达式。
JavaScript语言的Thunk函数
JavaScript语言是传值调用,它的Thunck函数含义有所不同。在JavaScript语言中,Thunk函数替换的不是表达式,而是多参数函数,将其替换成一个只接受回调函数作为参数的单参数函数。
//正常版本的readFile(多参数版本)
fs.readFile(fileName,callback);
//Thunk版本的readFile(单参数版本);
var Thunk = function(fileName){
return function(callback){
return fs.readFile(fileName,callback);
};
};
var readFileThunk = Thunk(fileName);
readFileThunk(callback);
上面代码中,fs模块的readFile方法是一个错参数函数,两个参数分别为文件名和回调函数。经过转换器处理,它变成了一个但参数函数,只接受回调函数作为参数。这个单参数版本,就叫做Thunk函数。
任何函数,只要参数有回调函数,就能写成Thunk函数的形式。下面是一个简单的Thunk函数转换器。
//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版本
const Thunk = function(fn){
return function(...args){
return function(callback){
return fn.call(this,...args,callback);
}
};
};
//使用上面的转化器,生生fs.readFile的Thunk函数。
var readFileThunk = Thunk(fs.readFile);
readFileThunk(fileA)(callback)
Thunkify模块
生产环境的转换器,建议使用Thunkify模块。
首先是安装。
$ npm install thunkify
使用方式如下。
var thunkify = require('thunkify');
var fs = require('fs');
var read = thunkify(fs.readFile);
read('package.json')(function(err,str){
//...
})
Thunkify的源码与上面的简单的转换器非常像。
function thunkfiy(fn){
return function(){
var args = new Array(arguments.length);
var ctx = this;
for(var i=0;i<args.length;++i){
args[i] = arguments[i];
}
return function(done){
var called;
args.push(function(){
if(called) return;
called = true;
done.apply(null,arguments);
});
try{
fn.apply(ctx,args);
}catch(err){
done(err);
}
}
}
}
它的源码主要多了一个检查机制,变量called确保回调函数只运行一次,请看下面的例子。
function f(a,b,callback){
var sum = a+b;
callback(sum);
callback(sum);
}
var ft = thunkify(f);
var print = console.log.bind(console);
ft(1,2)(print);
//上面代码中,由于thunkify只允许回调函数执行一次,所以只输出一行结果。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。