Thunk 函數是自動執行 Generator 函數的一種方法。異步
編譯器的「傳名調用」實現,每每是將參數放到一個臨時函數之中,再將這個臨時函數傳入函數體。這個臨時函數就叫作 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
函數求值便可。this
這就是 Thunk 函數的定義,它是「傳名調用」的一種實現策略,用來替換某個表達式。spa
JavaScript 語言是傳值調用,它的 Thunk 函數含義有所不一樣。在 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 函數。code
任何函數,只要參數有回調函數,就能寫成 Thunk 函數的形式。下面是一個簡單的 Thunk 函數轉換器。對象
// ES6版本 var Thunk = function(fn) { return function (...args) { return function (callback) { return fn.call(this, ...args, callback); } }; };
使用上面的轉換器,生成fs.readFile
的 Thunk 函數。blog
var readFileThunk = Thunk(fs.readFile); readFileThunk(fileA)(callback);
你可能會問, Thunk 函數有什麼用?回答是之前確實沒什麼用,可是 ES6 有了 Generator 函數,Thunk 函數如今能夠用於 Generator 函數的自動流程管理。ip
Generator 函數能夠自動執行。編譯器
function* gen() { // ... } var g = gen(); var res = g.next(); while(!res.done){ console.log(res.value); res = g.next(); }
上面代碼中,Generator 函數gen
會自動執行完全部步驟。
Thunk 函數真正的威力,在於能夠自動執行 Generator 函數。下面就是一個基於 Thunk 函數的 Generator 執行器。
function run(fn) { var gen = fn(); function next(err, data) { var result = gen.next(data); if (result.done) return; result.value(next); } next(); } function* g() { // ... } run(g);
上面代碼的run
函數,就是一個 Generator 函數的自動執行器。內部的next
函數就是 Thunk 的回調函數。next
函數先將指針移到 Generator 函數的下一步(gen.next
方法),而後判斷 Generator 函數是否結束(result.done
屬性),若是沒結束,就將next
函數再傳入 Thunk 函數(result.value
屬性),不然就直接退出。
有了這個執行器,執行 Generator 函數方便多了。無論內部有多少個異步操做,直接把 Generator 函數傳入run
函數便可。固然,前提是每個異步操做,都要是 Thunk 函數,也就是說,跟在yield
命令後面的必須是 Thunk 函數。
Thunk 函數並非 Generator 函數自動執行的惟一方案。由於自動執行的關鍵是,必須有一種機制,自動控制 Generator 函數的流程,接收和交還程序的執行權。回調函數能夠作到這一點,Promise 對象也能夠作到這一點。