CO 源码解析一

1,642 阅读3分钟
原文链接: github.com

最近用到co,顺带看co源码 学习了一波。
在开始源码分析之前,先了解了解co到底是个什么东西,为什么需要用到它。

co是个什么东东?

表面来看co就是c和o的结合,hh开个玩笑,co是一个generator函数(生成器函数)的自动执行函数,什么是自动执行下面再说,先说说生成器函数的使用,当调用一个生成器函数时,函数会返回一个iterator对象并通过next()得到{value: xxx, done: xxx} 这类的数据,value值为yield后面处理返回的结构,如yield 'aa'则{value: aa, done: xxx}, done值判断生成器函数是否处理yield,值为false表示没处理完,反之。举个栗子详细说明value和done。

    // 生成器函数都是具有*的函数
    function* gen() {
        yield 'yuyuyu';
        yield 'mamama';
        return true;
    }

    // 获取迭代器对象
    var iterator = gen();

    // 调用next()获取{value: xxx, done: xxx}对象
    var p1 = iterator.next();
    var p2 = iterator.next();
    var p3 = iterator.next();
    console.log(p1);// {value: 'yuyuyu', done: false}
    console.log(p2);// {value: 'mamama', done: false}
    console.log(p3);// {value: true, done: true}


co有什么用呢?

Tj大神根据生成器函数的这个特性,活生生的把他拿来处理异步回调地狱的噩梦。由上面的栗子可以知道,用户每次都需要手动的调用next()方法来移动迭代器的指针,co就是设计就是不需要手动的调用next函数,他会根据你传入生成器函数自动的执行next函数,直到done为true为止。
co是根据生成器的特性和Promise结合创造出来的一个自动生成器。

generator和Promise是怎么样结合处理异步的。

有人说Promise都可以用来处理异步回调嵌套了,为什么还要和generator结合呢,我使用Promise处理异步第一感觉就是then是不是调用太多了。举个使用Promise的栗子

function col() {
    return new Promise(function(resolve, reject) {
        setTimeout(function(){
            resolve(1111)
        },0)
    })
}
col().then(function(res) {
    console.log(res);
    return Promise.resolve(2222)
})
.then(function(res) {
    console.log(res)
})
.catch(function(err) {
    console.error(err);
})

以上有两个异步处理,相应的调了两次then获取res,如果有多个异步处理,而这些异步处理需要有同步的关系,也就是说调用某个异步需要另一个异步处理返回的结果,这样就造成了有多少个相关的异步调用则就需要写多少个then。而generator和promise(thunk函数通过callback与gennerator结合也是可以的)结合则更加优雅些。

理解co需要了解Promise的规范

Promise有三个状态,pending(初始),fulfill(完成),reject(失败)
三个状态的相互关系

  • pending状态可以转化成fulfill状态,也就是resolve()完成
  • pending状态可以转化成reject状态,调用reject
  • fulfill和reject状态不能相互转化
  • 状态的转化是不可以逆的

如下是Promise和generator结合的简单栗子

通过手工调用next函数

function* gen() {
    var h = yield Promise.resolve('hello');
    console.log(h);
    var w = yield Promise.resolve('world');
    console.log(w);
}
function co(gen) {
    var iter = gen();
    var p1 = iter.next();
    p1.value.then(function(res) {
        var p2 = iter.next(res);
        p2.value.then(function(res) {
            var p3 = iter.next(res);
            console.log(p3.done)
        })

    })
}

co(gen);

以上通过手动调用next函数来实现同步的逻辑并将res作为传入到next函数中作为上一个yield 表达式的返回结果。

自动执行调用next函数

function* gen() {
    var h = yield Promise.resolve('hello');
    console.log(h);
    var w = yield Promise.resolve('world');
    console.log(w);
}
function co(gen) {
    // 源码此处需要判断gen是否是生成器函数
    var iter = gen();
    var ret = iter.next();
    function next(ret) {
        ret.value.then(function(res) {
            ret = iter.next(res);
            if(ret.done === true) return;
            next(ret);
        })
    }
    next(ret);

}
co(gen);

以上为next函数的自动执行,co函数里面定义的next函数是自动执行的关键。