co源码分析
前言
Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态。调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是遍历器对象(Iterator Object)。
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }
1. Generator 函数的异步应用
Generator 函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权(即暂停执行)。整个 Generator 函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方,都用yield语句注明。
function* gen(x) {
var y = yield x + 2;
return y;
}
var g = gen(1);
g.next() // { value: 3, done: false }
g.next() // { value: undefined, done: true }
2. co 模块
co 模块是著名程序员 TJ Holowaychuk 于 2013 年 6 月发布的一个小工具,用于 Generator 函数的自动执行。Generator 函数只要传入co函数,就会自动执行。co函数返回一个Promise对象,因此可以用then方法添加回调函数。
var co = require('co');
co(function *(){
// yield any promise
var result = yield Promise.resolve(true);
}).catch(onerror);
co(function *(){
// resolve multiple promises in parallel
var a = Promise.resolve(1);
var b = Promise.resolve(2);
var c = Promise.resolve(3);
var res = yield [a, b, c];
console.log(res);
// => [1, 2, 3]
}).catch(onerror);
3. co 模块的源码
co 函数接受 Generator 函数作为参数,返回一个 Promise 对象。
function co(gen) {
var ctx = this;
var args = slice.call(arguments, 1);
return new Promise(function(resolve, reject) {
});
}
在 Promise 对象里面,co 先检查参数gen是否为 Generator 函数。如果是,就执行该函数,得到一个内部指针对象;如果不是就返回,并将 Promise 对象的状态改为resolved。
function co(gen) {
var ctx = this;
var args = slice.call(arguments, 1);
return new Promise(function(resolve, reject) {
if (typeof gen === 'function') gen = gen.apply(ctx, args);
if (!gen || typeof gen.next !== 'function') return resolve(gen);
});
}
然后 co 将 Generator 函数的内部指针对象的next方法,包装成onFulfilled函数。这主要是为了能够捕捉抛出的错误。
function co(gen) {
var ctx = this;
var args = slice.call(arguments, 1);
return new Promise(function(resolve, reject) {
if (typeof gen === 'function') gen = gen.apply(ctx, args);
if (!gen || typeof gen.next !== 'function') return resolve(gen);
onFulfilled();
function onFulfilled(res) {
var ret;
try {
ret = gen.next(res);
} catch (e) {
return reject(e);
}
next(ret);
}
});
}
最后,就是关键的next函数,它会反复调用自身。
function co(gen) {
var ctx = this;
var args = slice.call(arguments, 1);
return new Promise(function(resolve, reject) {
if (typeof gen === 'function') gen = gen.apply(ctx, args);
if (!gen || typeof gen.next !== 'function') return resolve(gen);
onFulfilled();
function onFulfilled(res) {
var ret;
try {
ret = gen.next(res);
} catch (e) {
return reject(e);
}
next(ret);
}
function next(ret) {
// 检查当前是否为 Generator 函数的最后一步,如果是就返回。
if (ret.done) return resolve(ret.value);
// 确保每一步的返回值,是 Promise 对象。
var value = toPromise.call(ctx, ret.value);
// 使用then方法,为返回值加上回调函数,然后通过onFulfilled函数再次调用next函数。
if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
// 在参数不符合要求的情况下将 Promise 对象的状态改为rejected,从而终止执行。
return onRejected(
new TypeError('You may only yield a function, promise, generator, array, or object, ' +'but the following object was passed: "' + String(ret.value) +'"'));}
});
}
co源码里几个重要的函数
// 参数转化为Proimse
function toPromise(obj) {
if (!obj) return obj;
if (isPromise(obj)) return obj;
//是generator function或generator,则用co调用,返回一个promise
if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj);
// 函数转化为promise
if ('function' == typeof obj) return thunkToPromise.call(this, obj);
// 数组转化为promise
if (Array.isArray(obj)) return arrayToPromise.call(this, obj);
// 对象转化为promise
if (isObject(obj)) return objectToPromise.call(this, obj);
return obj;
}
//thunk函数转promise
function thunkToPromise(fn) {
var ctx = this;
return new Promise(function (resolve, reject) {
fn.call(ctx, function (err, res) {
if (err) return reject(err);
if (arguments.length > 2) res = slice.call(arguments, 1);
resolve(res);
});
});
}
// 数组转化为promise
function arrayToPromise(obj) {
return Promise.all(obj.map(toPromise, this));
}
// 对象转化为promise
function objectToPromise(obj){
//构造一个同类型对象
var results = new obj.constructor();
var keys = Object.keys(obj);
var promises = [];
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
//对象里面的所有value转换为promise
var promise = toPromise.call(this, obj[key]);
//是promise,就通过defer方法将promise执行结果放到results中
if (promise && isPromise(promise)) defer(promise, key);
//不是promise,就仅记录value
else results[key] = obj[key];
}
return Promise.all(promises).then(function () {
return results;
});
function defer(promise, key) {
// predefine the key in the result
results[key] = undefined;
promises.push(promise.then(function (res) {
results[key] = res;
}));
}
}
//判断是promise
function isPromise(obj) {
return 'function' == typeof obj.then;
}
//判断是generator
function isGenerator(obj) {
return 'function' == typeof obj.next && 'function' == typeof obj.throw;
}
//判断是generator function,其返回值是generator
function isGeneratorFunction(obj) {
var constructor = obj.constructor;
if (!constructor) return false;
if ('GeneratorFunction' === constructor.name || 'GeneratorFunction' === constructor.displayName) return true;
return isGenerator(constructor.prototype);
}
//判断是对象
function isObject(val) {
return Object == val.constructor;
}