最近在学习使用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