1、co是个啥?
看原文档、稀里哗啦写了一堆,英文也不认识。但是从中可以get出一段关键的信息:“generator函数(生成器函数)的自动执行函数”。
2、疑问?
generator不能自动执行么,还非得要个第三方的库?带着疑问我去看了一下它的基本用法。
function* gen() {
yield '1';
yield '2';
return true;
}
var iterator = gen();
var p1 = iterator.next();
var p2 = iterator.next();
var p3 = iterator.next();
console.log(p1);// {value: '1', done: false}
console.log(p2);// {value: '2', done: false}
console.log(p3);// {value: true, done: true}
写了一遍发现确实有点麻烦,每次迭代都要.next()才能继续下一步的操作,直到done为true时停止。
3、co是如何解决这个问题的?
经过源码了解co就是根据生成器的特性和Promise结合创造出来的一个自动生成器。也就是说不需要手动的调用next函数,他会根据你传入生成器函数自动的执行next函数,直到done为true为止。
核心代码:
function co(gen) {
var ctx = this;
var args = slice.call(arguments, 1)
return new Promise(function(resolve, reject) {
// 把参数传递给gen函数并执行
if (typeof gen === 'function') gen = gen.apply(ctx, args);
// 如果不是函数 直接返回
if (!gen || typeof gen.next !== 'function') return resolve(gen);
onFulfilled();
//重复执行next
function onFulfilled(res) {
var ret;
try {
ret = gen.next(res);
} catch (e) {
return reject(e);
}
next(ret);
}
function onRejected(err) {
var ret;
try {
ret = gen.throw(err);
} catch (e) {
return reject(e);
}
next(ret);
}
function next(ret) {
// 如果done为true,直接返回
if (ret.done) return resolve(ret.value);
// 将结果封装成promise对象。
var value = toPromise.call(ctx, ret.value);
// 判断 value 是否为 Promise,如果是就返回 then 继续执行
if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
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内部要用Promise封了一层?
我的感觉就是使用Promise更容易的捕获异常,也就是当任务失败,就执行 onRejected 函数,成功执行 onFullfilled 函数。
4、收获
我觉得本次看源码最大的收货就是学会了一些判断类型的小技巧。
例如Promise是这样判断的。
function isPromise(obj) {
return 'function' == typeof obj.then;
}
判断对象还有这种操作?
function isObject(val) {
return Object == val.constructor;
}
头一次接触generator判断。
function isGenerator(obj) {
return 'function' == typeof obj.next && 'function' == typeof obj.throw;
}
还有一些call的使用,之前不太会用。
5、感想
读源码感觉就像发现新大陆一样,原来代码还可以这样写!!!
可以开阔自己的视野,虽然看的是一篇源码,但是在看之前也需要补充大量的相关知识,完善自己的知识体系,最重要的是可以灵活的运用在自己的项目中,对自己是一个不错的提升。
6、参考链接
1、https://github.com/mqyqingfeng/Blog/issues/99 ES6 系列之 Generator 的自动执行
2、https://juejin.cn/post/6844904088220467213 学习 koa 源码的整体架构,浅析koa洋葱模型原理和co原理