对于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 gen
为 function *()
部分,下面是函数next()
的声明,之后执行了next()
函数,此时的err
和result
均为空。
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.done
为false
, 执行step.value(next);
,即执行
function(callback) {
require('fs').readFile('./file/a.txt', 'utf8', callback);
};
函数,并next
作为callback
传入。readFile
传入回调函数的参数为err
和data
,因此在读取完文件后,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
了,于是执行到return
,step.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