在 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