理解co执行逻辑
发布在nodejs学习笔记2014年11月11日view:10503
在文章任何区域双击击即可给文章添加【评注】!浮到评注点上可以查看详情。

对于co的一个最简版的实现,代码如下 (https://gist.github.com/iwillwen/8488050

function co(generator) {
  return function(fn) {
    var gen = generator();
    function next(err, result) {
        if(err){
            return fn(err);
        }
        var step = gen.next(result);
        if (!step.done) {
            step.value(next);
        } else {
            fn(null, step.value);
        }
    }
    next();
   }
}

为了理解这个代码,来看一个例子

var co = require('./co');
// wrap the function to thunk
function readFile(filename) {
    return function(callback) {
        require('fs').readFile(filename, 'utf8', callback);
    };
}

co(function * () {
    var file1 = yield readFile('./file/a.txt');
    var file2 = yield readFile('./file/b.txt');

    console.log(file1);
    console.log(file2);
    return 'done';
})(function(err, result) {
    console.log(result)
});

现在就执行顺序来说明一下

1.首先是以下部分

 co(function * () {
        var file1 = yield readFile('./file/a.txt');
        var file2 = yield readFile('./file/b.txt');

        console.log(file1);
        console.log(file2);
        return 'done';
    })

这部分代码执行了co函数,并把function*()作为参数generator传入,也就是说,整个部分变成了

function(fn) {
    var gen = generator();
    function next(err, result) {
        if(err){
            return fn(err);
        }
        var step = gen.next(result);
        if (!step.done) {
            step.value(next);
        } else {
            fn(null, step.value);
        }
    }
    next();
   }(function(err, result) {
    console.log(result)
})

这个部分执行这个function,并把

function(err, result) {
        console.log(result)
    }

作为fn的实参传入。

2. 执行过程中,var genfunction *()部分,下面是函数next()的声明,之后执行了next()函数,此时的errresult均为空。

3. err为空,因此if(err)不执行,执行 var step = gen.next(result);,由于result这里为空,因此

var step = gen.next(result)
step.value
=readFile('./file/a.txt')
=function(callback) {
        require('fs').readFile('./file/a.txt', 'utf8', callback);
    };

4. step.donefalse, 执行step.value(next);,即执行

function(callback) {
            require('fs').readFile('./file/a.txt', 'utf8', callback);
        };

函数,并next作为callback传入。readFile传入回调函数的参数为errdata,因此在读取完文件后,err和a文件的内容(作为result的实参)传入了next函数并执行。

5.与2,3类似,执行var step = gen.next(result),这一步由于result的值为a文件的内容,因此file1被赋值为a文件的内容(next方法可以带一个参数,该参数就会被当作上一个yield语句的返回值。)。与4类似,err和b文件的内容(作为result的实参)传入next函数并执行。

6.再次执行到var step = gen.next(result)的时候,file2被赋予文件b的内容,由于generator下面没有yield了,于是执行到returnstep.value='done',step.done=true,函数从上次yield语句停下的地方,一直执行到return语句(如果没有return语句,就执行到函数结束)。(next方法返回的对象的value属性,就是紧跟在return语句后面的表达式的值(如果没有return语句,则value属性的值为undefined),done属性的值true,表示遍历已经结束。)

7.执行fn(null, step.value);

function(err, 'done') {
    console.log('done');
}();

8.因此代码的执行结果为

content in a.txt

content in b.txt

done

评论
发表评论
3年前
添加了一枚【评注】:请教这个gen为什么能调用next()?
3年前
添加了一枚【评注】:这里少了括号
4年前

其实这种基于纤程的异步架构在lua/python等语言里已经很成熟了,当然了lua更狠,代码里甚至都不用写yield关键字

4年前

好文章

WRITTEN BY
OT元旦
太阳出来,为了生活出去,太阳落了,为了爱情回去。
TA的新浪微博
PUBLISHED IN
nodejs学习笔记

关于个人学习nodejs的一些笔记

我的收藏