在 JavaScript 语言中,Thunk 函数替换的不是表达式,而是多参数函数,将其替换成单参数的版本,且只接受回调函数做为参数。html
// 正常版本的readFile(多参数版本) fs.readFile(fileName, callback); // Thunk版本的readFile(单参数版本) var readFileThunk = Thunk(fileName); readFileThunk(callback); var Thunk = function (fileName){ return function (callback){ return fs.readFile(fileName, callback); }; };
以读取文件为例。下面的 Generator 函数封装了两个异步操做。shell
var fs = require('fs'); var thunkify = require('thunkify'); var readFile = thunkify(fs.readFile); var gen = function* (){ var r1 = yield readFile('/etc/fstab'); // 2. 读取文件一 console.log(r1.toString()); var r2 = yield readFile('/etc/shells');// 5. 读取文件二 console.log(r2.toString()); };
手动执行方式:异步
var g = gen();// 0. 初始化 var r1 = g.next();// 1. 执行下一步,返回的r1就是generator指针:{value, done},而这里的value其实就是一个thunk函数,这个thunk函数以回调函数做为参数 r1.value(function(err, data){// 3. 文件一读取完成的回调函数 if (err) throw err; var r2 = g.next(data);// 4. 执行下一步 r2.value(function(err, data){ // 文件二读取完成的回调函数 if (err) throw err; g.next(data); }); });
总的来讲,其实就是利用thunk函数,把须要作的操做和对应的回调函数,从fn(operation, callback)
改为了fn(operation)(callback)
的形式。函数
为何要这么作?
是由于generator函数在yield返回后,不会自动往下执行,若是写成:ui
var gen = function* (){ var r1 = yield readFile('/etc/fstab', gen.next()); // 这时gen尚未初始化,不是一个generator指针,因此没有next方法,而gen() !== gen(),因此也不能写成gen().next() console.log(r1.toString()); };
也无法自动执行,因此将回调函数分离到第二步,而后在回调函数里(这时generator确定已经初始化完了,否则无法执行到回调函数)执行generator指针的next方法,走到下一步。指针
总结:执行value方法本质上至关于注册一个回调函数,而generator函数结合thunk函数就是一种更直观的注册回调函数的方式。generator函数负责异步执行(交出执行权),而thunk函数负责注册回调(返回执行权,执行下一步),二者结合从而自动执行generator函数。code
若是有任何理解不稳当的地方,欢迎指正交流。htm