co是如何工作的
co是用來自動執行generator函數的工具。那麼,首先你需要知道generator。Generator的好處是可以在定義函數時候就打上“斷點”,調用函數時候可以在斷點的地方暫停函數的執行。Generator帶來的問題是如何控制什麼時候進行下一步調用。co就是解決這個問題的。
下面所有代碼可以到github上查看源代碼。
fs = require('fs');
co = require('co');
//thunkify是一個nodejs庫,下面我會展示其簡單的實現原理,如果這步你看不懂,可以暫時認爲readFile爲一個普通函數
var readFile = thunkify(fs.readFile);
var gen = function* (){
var r1 = yield readFile('./hi.txt');
console.log(r1.toString());
var r2 = yield readFile('./hello.txt');
console.log(r2.toString());
};
var gen2 = function* (){
var r1 = yield 1;
console.log(r1.toString());
var r2 = yield 2;
console.log(r2.toString());
};
console.log('------------------run co--------------------');
console.log('-----------gen------------');
co(gen);
console.log('-----------gen2-----------');
co(gen2);
執行結果:
由這個執行結果我們至少可以看出兩點:1)普通的generator是不能放到co裏進行自動執行的(gen2沒有自動執行) 2)co可以實現異步執行(三個console前後打出,才返回gen執行的結果)
那麼,什麼樣的generator纔可以被co包裹後自動執行?
co實現generator自動執行的條件
co的github說的很明白,需要yield後面的數據類型爲:
- promises
- thunks (functions)
- array (parallel execution)
- objects (parallel execution)
- generators (delegation)
- generator functions (delegation)
最後兩種類型是官方建議避免使用的。array或者object的每個子元素依然應該是promises或者thunks。瞭解promise。
thunk在javascript中可以理解爲,可以將一個進行一定操作並調用一個回調函數的函數(nodejs中很多,如:fs),拆分成兩步。上面例子中的thunkify
就是一個thunk,其聲明如下:
function thunkify(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);
}
};
};
}
執行var readFile = thunkify(fs.readFile);
的時候,返回一個函數readFile
,其接受一個參數,爲要讀取文件的地址,並再次返回一個函數,該函數的參數爲讀取文件完畢後要執行的回調函數,傳入回調函數後讀取操作纔開始執行。thunk就是把回調函數的寫法變成兩次傳參而已。