1、co是个啥?

看原文档、稀里哗啦写了一堆,英文也不认识。但是从中可以get出一段关键的信息:“generator函数(生成器函数)的自动执行函数”。

2、疑问?

generator不能自动执行么,还非得要个第三方的库?带着疑问我去看了一下它的基本用法。

  1. function* gen() {
  2. yield '1';
  3. yield '2';
  4. return true;
  5. }
  6. var iterator = gen();
  7. var p1 = iterator.next();
  8. var p2 = iterator.next();
  9. var p3 = iterator.next();
  10. console.log(p1);// {value: '1', done: false}
  11. console.log(p2);// {value: '2', done: false}
  12. console.log(p3);// {value: true, done: true}

写了一遍发现确实有点麻烦,每次迭代都要.next()才能继续下一步的操作,直到done为true时停止。

3、co是如何解决这个问题的?

经过源码了解co就是根据生成器的特性和Promise结合创造出来的一个自动生成器。也就是说不需要手动的调用next函数,他会根据你传入生成器函数自动的执行next函数,直到done为true为止。

核心代码:

  1. function co(gen) {
  2. var ctx = this;
  3. var args = slice.call(arguments, 1)
  4. return new Promise(function(resolve, reject) {
  5. // 把参数传递给gen函数并执行
  6. if (typeof gen === 'function') gen = gen.apply(ctx, args);
  7. // 如果不是函数 直接返回
  8. if (!gen || typeof gen.next !== 'function') return resolve(gen);
  9. onFulfilled();
  10. //重复执行next
  11. function onFulfilled(res) {
  12. var ret;
  13. try {
  14. ret = gen.next(res);
  15. } catch (e) {
  16. return reject(e);
  17. }
  18. next(ret);
  19. }
  20. function onRejected(err) {
  21. var ret;
  22. try {
  23. ret = gen.throw(err);
  24. } catch (e) {
  25. return reject(e);
  26. }
  27. next(ret);
  28. }
  29. function next(ret) {
  30. // 如果done为true,直接返回
  31. if (ret.done) return resolve(ret.value);
  32. // 将结果封装成promise对象。
  33. var value = toPromise.call(ctx, ret.value);
  34. // 判断 value 是否为 Promise,如果是就返回 then 继续执行
  35. if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
  36. return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
  37. + 'but the following object was passed: "' + String(ret.value) + '"'));
  38. }
  39. });
  40. }

到这里基本上就结束了本次的源码之旅,但是还是有些疑问,为什么co内部要用Promise封了一层?

我的感觉就是使用Promise更容易的捕获异常,也就是当任务失败,就执行 onRejected 函数,成功执行 onFullfilled 函数。

4、收获

我觉得本次看源码最大的收货就是学会了一些判断类型的小技巧。

例如Promise是这样判断的。

  1. function isPromise(obj) {
  2. return 'function' == typeof obj.then;
  3. }

判断对象还有这种操作?

  1. function isObject(val) {
  2. return Object == val.constructor;
  3. }

头一次接触generator判断。

  1. function isGenerator(obj) {
  2. return 'function' == typeof obj.next && 'function' == typeof obj.throw;
  3. }

还有一些call的使用,之前不太会用。

5、感想

读源码感觉就像发现新大陆一样,原来代码还可以这样写!!!

可以开阔自己的视野,虽然看的是一篇源码,但是在看之前也需要补充大量的相关知识,完善自己的知识体系,最重要的是可以灵活的运用在自己的项目中,对自己是一个不错的提升。

6、参考链接

1、https://github.com/mqyqingfeng/Blog/issues/99 ES6 系列之 Generator 的自动执行
2、https://juejin.cn/post/6844904088220467213 学习 koa 源码的整体架构,浅析koa洋葱模型原理和co原理