Thunk 函数
1. 什么是 Thunk 函数?
JS 是传值调用,它的 Thunk 函数含义有所不同:在 JS 中,Thunk 函数替换的不是表达式,而是多参数函数,将其替换成只接受回调函数作为参数的单参数版本。
// 正常版本的 readFile(多参数)
fs.readFile(fileName, callback);
// Thunk 版本的 readFile(单参数)
const readFileThunk = Thunk(fileName);
readFileThunk(callback);
const Thunk = function (fileName) {
return function (callback) {
return fs.readFile(fileName, callback);
};
};
任何参数中有回调函数的函数,都能写成 Thunk 函数形式:
const Thunk = function (fn) {
return function () {
const args = Array.prototype.slice.call(arguments);
return function (callback) {
args.push(callback);
return fn.apply(this, args);
};
};
};
const readFileThunk = Thunk(fs.readFile);
readFileThunk(fileA)(callback);
2. 另一种实现:完全柯里化的 Thunk
如果希望 Thunk 工厂接受原始函数,调用时再依次填入参数与回调,写法更灵活:
const Thunk = function (fn) {
return function (...args) {
return function (callback) {
return fn.call(this, ...args, callback);
};
};
};
// 用法一:先传参数,再传回调
const fun = Thunk(args1, args2);
fun(callback);
// fn(...args, callback); 自动执行 Generator 函数, 先传入参数, 再传入回调函数
这种写法的特点是 Thunk 工厂先接
fn,然后链式调用:Thunk(fn)(...args)(callback),每一层都"收集"参数直到最后一层再统一执行。
3. Thunk 函数有什么用?
以前用处不大,但 ES6 有了 Generator 函数后,Thunk 函数可以用于 Generator 函数的自动流程管理。
真正的威力在于可以自动执行 Generator 函数:
function run(fn) {
const gen = fn();
function next(err, data) {
const result = gen.next(data);
if (result.done) return;
// result.value 是 Thunk 函数
result.value(next);
}
next();
}
run(gen);
run就是 Generator 的自动执行器。内部next先移到下一步(gen.next),判断是否结束(result.done),没结束就把next注入 Thunk 函数(result.value),结束则退出。
有了这个执行器,执行 Generator 函数方便很多。不管多少个异步操作,直接传入 run 即可(前提是每个异步操作都是 Thunk 函数)。
const gen = function* () {
const f1 = yield readFile('fileA');
const f2 = yield readFile('fileB');
// ...
};
run(gen);
4. 其他自动执行方案
- 回调函数:将异步操作包装成 Thunk 函数。
- Promise 对象:将异步操作包装成 Promise,用
then交回执行权。
co 函数库
co 可以让你不用编写 Generator 的执行器:
const co = require('co');
co(gen).then(function () {
console.log('Generator 函数执行完成');
});
co 的原理
Generator 函数是异步操作的容器,自动执行需要一种机制:当异步操作有结果时,自动交回执行权。
两种方法可以做到:
- 回调函数:将异步操作包装成 Thunk 函数,在回调里交回执行权。
- Promise 对象:将异步操作包装成 Promise,用
then交回执行权。
co 库其实就是把两种自动执行器(Thunk 函数 + Promise)包装成一个库。使用 co 的前提条件是:Generator 的 yield 命令后面只能是 Thunk 函数 或 Promise 对象。
来源整理自:我的有道云笔记



