精华 搞定 koa 之generator 与 co
发布于 10 年前 作者 demohi 19196 次浏览 最后一次编辑是 8 年前 来自 分享

koa 是由 tj大神利用 generator 开发的 web 框架。要理解 koa,首先要先了解 generator 与 co。作为搞定 koa 的第一篇,我们便谈谈这个。文章首发在boke.io

系列目录

  1. 搞定 koa 之generator 与 co
  2. 搞定 koa 之 co源码解析

generator介绍

 function* Gene(){
    yield 1;
    yield 2;
}
var gene = Gene();
console.log(gene.next());//{ value: 1, done: false }
console.log(gene.next());//{ value: 2, done: false }
console.log(gene.next());//{ value: undefined, done: true }
console.log(gene.next());//Error: Generator has already finished  经@Ralph-Wang提醒从 v0.11.13 开始不抛错了,返回{ value: undefined, done: true }

从上面我们可以看到

  • generator 的定义和函数类似,只是在 function 后面多了一个*
  • 调用generator 和调用函数一样,只是不像函数立即执行,而是会生成一个对象
  • generator 生成的对象存在一个 next 函数,调用 next 会返回 yield运算的结果对象,并停止。再次调用会在下一个 yield 处停止。
  • 当所有的 yield 被执行完,调用 next 函数会返回{ value: undefined, done: true }。再次调用会报错

generator 与异步

看完 generator 的介绍,你心里回想这跟异步有毛关系?不着急听我接着说

串行请求两个网页的代码

var request = require('request');
var a = {};
var b = {};
request('http://www.google.com', function (error, response, body) {
    if (!error && response.statusCode == 200) {
        a.response = response;
        a.body = body;
        request('http://www.yahoo.com', function (error, response, body) {
            if (!error && response.statusCode == 200) {
                b.response = response;
                b.body = body;
            }
        });
    }
});

我们再看看最终我们是如何利用 generator请求网页的

co(function *(){
  var a = yield request('http://google.com');
  var b = yield request('http://yahoo.com');
  console.log(a[0].statusCode);
  console.log(b[0].statusCode);
})()

上面的代码可以看到,co 里面传入了一个 generator,里面用 yield 调用异步函数。从逻辑上看,里面的异步被变成了同步。

co 到底有什么魔法?可以把我们从异步中解救出来?

想想 generator 的特性,当我们执行第一个 next 的时候,会调用第一个 request,此时我们去调用 request 的逻辑,然后把 generator 的实例穿进去。当第一个 request 逻辑完成时,在调用这个 generator 的 next,这样就到了第二个 yield 的逻辑了。当然这需要一些逻辑的封装,也就是 co 了。

根据上面的分析,我们大概可以写出下面的代码

function co(Gene){
    //先实例化一下
    var gene = Gene();
    //如果存在 next 函数
    if(gene.next){
        var fun = gene.next();//把异步函数返回过来,好继续封装
        //fun 处理完,再调用 gene.next()
        //...
    }
}

从上面的代码可以看出

  • yield 后面的内容需要返回一个异步函数,这样我们才可进一步封装异步处理的逻辑。
  • fun 需要可以传入参数,这样才可以处理多个异步
  • 需要一个递归调用,这样才可以持续调用 next,同时根据 next 返回对象中的done属性停止逻辑
  • 需要考虑错误处理

今天就到这里,下一篇详细讲一下 co 的源码

参考资料

学习ES6生成器(Generator)

解决回调金字塔! Harmony Generator, yield, ES6, co框架学习

联系我

微博 GitHub

6 回复

node 0.11.14.

function* Gene(){
    yield 1;
    yield 2;
}
var gene = Gene();
console.log(gene.next());//{ value: 1, done: false }
console.log(gene.next());//{ value: 2, done: false }
console.log(gene.next());//{ value: undefined, done: true }
console.log(gene.next());//{ value: undefined, done: true }

@Ralph-Wang 感谢提醒 不再报错了。。我的版本是v0.11.11

@demohi 嗯, 我又跑去试了=m= … 从 v0.11.13 开始不抛错的

@Ralph-Wang 好敬业 我改一下

@demohi QA 的职业病 =,=

回到顶部