co源码解读
最近在学习使用Koa.在看官网的例子的时候时候看到它的中间件的级联模式是通过生成器实现的,之后了解到Koa的级联是通过Co这个库实现的,下面就是对这个库的代码的主要流程的部分解读
这个是infoq的深入浅出ES6中生成器的一章 生成器基础
可以通过下面的代码片段去大致的理解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状态。可以通过下面的代码简单的理解它的使用
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对象的封装。
下面就来理解下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
posted on 2017-02-25 15:41 icantunderstand 阅读(378) 评论(0) 编辑 收藏 举报