最近在學習使用Koa.在看官網的例子的時候時候看到它的中間件的級聯模式是經過生成器實現的,以後瞭解到Koa的級聯是經過Co這個庫實現的,下面就是對這個庫的代碼的主要流程的部分解讀git
1.生成器基礎es6
2.代碼解讀github
這個是infoq的深刻淺出ES6中生成器的一章 生成器基礎api
能夠經過下面的代碼片斷去大體的理解Co函數的執行過程 生成器能夠經過next來實現任務的串行執行 next方法返回一個對象 有value屬性(yield執行的值) done 是否還有未執行的yield邏輯 同時還能將參數經過next進行傳遞 供下面的邏輯調用數組
function *test() { var x = yield 1; yield console.log(x); } var a = test(); var temp = a.next(); a.next(temp.value) //1
function objectToPromise(obj){ var results = new obj.constructor(); var keys = Object.keys(obj); //Object.keys()返回一個對象全部可枚舉屬性的數組 var promises = [];//保存對象中promise運行的結果 for (var i = 0; i < keys.length; i++) { var key = keys[i];// var promise = toPromise.call(this, obj[key]); if (promise && isPromise(promise)) defer(promise, key);//將原對象屬性中Promise的與數組關聯 else results[key] = obj[key]; } return Promise.all(promises).then(function () { return results; });//Promise接受一個Promise對象的數組做爲參數 當數組裏面所有變成resolve或者reject狀態 function defer(promise, key) { // predefine the key in the result results[key] = undefined; promises.push(promise.then(function (res) { results[key] = res; //romise的then方法不單單是註冊resolve的回調 還會將回調函數的返回值進行變換 返回promsie 對象 })); } }
Co將中間的結果和函數都進行了Promise的封裝 主要看下這個objectToPromise(obj) 功能就是將一個對象轉換爲Promise對象 這裏須要理解這兩個方法 Promise.all 它接受一個Promsie數組 當數組中所有爲resolve時,它就變成resolve,當其中有一個出現reject的時候,就進入reject狀態。能夠經過下面的代碼簡單的理解它的使用app
var promise1 = new Promise(function(resolve,reject){ resolve(1); }); var promise2 = new Promise(function(resolve,reject){ resolve(2); }); Promise.all([promise1,promise2]).then(function(){ console.log('ok'); });
接下來講說這個defer(promise,key) 它的功能就是將原來對象的值裝換爲promise以後,經過數組的方式傳遞給外部異步
function defer(promise, key) { // predefine the key in the result results[key] = undefined; promises.push(promise.then(function (res) { results[key] = res; promise的then方法不單單是註冊resolve的回調 還會將回調函數的返回值進行變換 返回promsie 對象 })); }
promise.then 這個方法是返回一個Promise對象 這樣經過then方法將全部鍵值的執行結果都轉換爲Promsie對象 在經過數組的方式傳遞給外部 當全部鍵值執行都爲resolve的時候 就將總體的結果返回給外部 也就完成了對obj對象的封裝。async
下面就來理解下Co函數
function co(gen) { var ctx = this; var args = slice.call(arguments, 1); // we wrap everything in a promise to avoid promise chaining, // which leads to memory leak errors. // see https://github.com/tj/co/issues/180 return new Promise(function(resolve, reject) { if (typeof gen === 'function') gen = gen.apply(ctx, args); if (!gen || typeof gen.next !== 'function') return resolve(gen); onFulfilled(); /** * @param {Mixed} res * @return {Promise} * @api private */ function onFulfilled(res) { var ret; try { ret = gen.next(res); } catch (e) { return reject(e); } next(ret); return null; } /** * @param {Error} err * @return {Promise} * @api private */ function onRejected(err) { var ret; try { ret = gen.throw(err); } catch (e) { return reject(e); } next(ret); } /** * Get the next value in the generator, * return a promise. * * @param {Object} ret * @return {Promise} * @api private */ //co函數的核心就是這個next理解 經過next傳遞將上一個generator執行的結果往下傳遞而且進行了Promise的封裝 function next(ret) { if (ret.done) return resolve(ret.value); var value = toPromise.call(ctx, ret.value); if (value && isPromise(value)) return value.then(onFulfilled, onRejected); return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, ' + 'but the following object was passed: "' + String(ret.value) + '"')); } }); }
參考 Generator與異步編程 Co