单纯的co实现
co的初衷就是自执行generator,并且提供对Promise对象的支持
function co(generator, data) {
const result = generator.next(data) // 此处执行generator的next,获取结果
if(result.done) { // 若generator已执行到末尾,则结束
return
} else { // 否则,针对其value进行处理
const value = result.value
if(value instanceof Promise) { // 若value为Promise对象
value.then(data => { // 待此Promise对象resolve后,再继续执行下面的next
co(generator, data) // 继续执行co,其本质是执行generator的next
})
} else {
co(generator, value) // value为普通值,则直接继续执行co
}
}
}
koa1.x中间件的实现
koa1的中间件是基于generator实现的,所以其中也包括变种的co,其著名的洋葱模型如下:


实现代码如下:
let index = 0, // 中间件数组的序号,用于标记执行进度
middleware = [], // 中间件数组,剥洋葱自内而外的过程会遍历它,use方法将会填充它
glist = [], // 中间件在yield next之后的generator保存在这个数组中,等待以后执行,剥洋葱自外而内的阶段会遍历它
NEXT = {}, // NEXT标识,对象类型,以参数形式传给中间件,当中间件yield next的时候,就表示要执行下一个中间件
ctx // 此变量代表koa1.x的上下文
function use(fn) { // use的本质是将中间件generator推进middleware数组,参数fn为generator函数
middleware.push(fn);
}
function run() { // 执行所有中间件的方法
next(); // 递归执行next方法
function next() {
const mid = middleware[index] // 从0开始,依次取出中间件
if(mid) { // 若存在此中间件
const gen = mid.bind(ctx, NEXT) // 为中间件方法绑定ctx上下文,并传入NEXT标识
co(gen) // co执行此中间件生成的generator
} else { // 若不存在此中间件,则说明由内而外的阶段已经结束,下面进入剥洋葱由外而内的阶段
index-- // index自减
const gen = glist[index] // 找到紧邻的内层generator,执行
co(gen)
}
}
function co(gen, data) {
const result = gen.next(data); // 执行next,且如果有data,传入data
if(result.done) { // 此generator已执行完,此情况一定为剥洋葱由外而内的阶段
index-- // 由外而内,故index自减
const gen = glist[index] // 找到紧邻的内层generator,执行
co(gen)
return
}
const value = result.value // 判断generator的value
if(value instanceof Promise) { // 若为Promise对象
value.then(data => { // 则在此Promise完成resolve后,再继续执行generator
co(gen, data)
})
} else if(value === NEXT) { // 此处比较NEXT标识,若相等则说明调用了yield next,则进行剥洋葱由内而外的下一阶段
glist.push(gen) // 保存未执行完的generator对象,等待未来由外而内阶段时再来执行
index++ // index自增
next() // 继续进行剥洋葱由内而外
} else { // 此情况为generator的普通yield,则继续执行generator
co(gen, value)
}
}
}
网友评论