co4.0以前都是返回的thunk函數
以後的都是返回promisehtml
thunk:在 JavaScript 語言中,Thunk 函數替換的是將多參數函數,替換成單參數的版本,且只接受回調函數做爲參數。node
// 正常版本的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); }; };
生產環境中,能夠使用thunkify將函數轉換爲thunk 函數git
問題:github
爲何node 裏面大部分的callback都是第一個參數是err呢?json
爲何要作thunk 轉換呢?redux
在redux裏面也有thunk middleware,這個thunk是什麼意思呢?promise
co的原理很簡單,就是將傳入的generator function 轉換爲一個thunk,而且轉換後thunk 的generator的每一個value值做爲下一個狀態的輸入app
function co(fn) {koa
var isGenFun = isGeneratorFunction(fn); return function (done) {//返回的thunk函數,done 做爲回調函數 var ctx = this; // in toThunk() below we invoke co() // with a generator, so optimize for // this case var gen = fn; //gen function 轉換爲generator if (isGenFun) { var args = slice.call(arguments), len = args.length; var hasCallback = len && 'function' == typeof args[len - 1]; done = hasCallback ? args.pop() : error; gen = fn.apply(this, args); } else { done = done || error; } //函數執行的時候就會執行next函數,進入函數體裏面 next(); // #92 // wrap the callback in a setImmediate // so that any of its errors aren't caught by `co` function exit(err, res) { setImmediate(done.bind(ctx, err, res)); } function next(err, res) { var ret; // multiple args if (arguments.length > 2) res = slice.call(arguments, 1); // error if (err) { try { ret = gen.throw(err); } catch (e) { return exit(e); } } // ok if (!err) { try { ret = gen.next(res); } catch (e) { return exit(e); } } // done if (ret.done) return exit(null, ret.value); // normalize ret.value = toThunk(ret.value, ctx); // run if ('function' == typeof ret.value) { var called = false; try { //好比執行yield readFile('test.json'), ret.value就是readFile函數,函數接受一個callback,callback調用next方法,講readFile的結果傳入了next函數 ret.value.call(ctx, function(){ //這裏能夠防止next函數被屢次執行 if (called) return; called = true; next.apply(ctx, arguments); }); } catch (e) { setImmediate(function(){ if (called) return; called = true; next(e); }); } return; } // invalid next(new Error('yield a function, promise, generator, array, or object')); } } }
經過上面的co源碼分析,能夠看下面的例子函數
co(function *() { var file = yield readFile('test.json'); //這裏的file是經過gen.next() 賦值的 console.log(file); var ret = yield writeFile(file, 'dest.json'); console.log(ret); })
瞭解了這些基本概念後就能夠進入koa的源碼閱讀了,具體的能夠參考下一篇。