【手写 Promise 源码】第十六篇 - 了解 co 库

777 阅读2分钟

一,前言

上篇,主要介绍了 generator 生成器函数的使用和实现原理,主要涉及以下几个点:

  • generator 简介:特性、用法、功能分析;
  • generator 实现原理分析;

前面的 Promise 仅仅是对异步回到进行了优化,在 Promise 的内部依然还是存在回调的,所以实际上并没有解决“回调地狱”的问题;

最终解决方案还要使用 async/await,async/await 是基于 generator 的语法糖,而为了搞清楚 async/await 的原理,需要先对 generator、co 进行了解;

本篇,继续介绍 co 库:自动执行 Generator 生成器函数;


二,co 库的简介

1,co 库的作用

  • 用于自动执行 Generator 生成器函数; 备注:co 库的作者是 TJ Holowaychuk,而他同时也是 express\koa 的作者;

2,co 库的使用

  • 安装 co 库 : npm install co
  • 测试场景:读取 a.txt得到结果 b.txt;读取 b.txt得到结果 c;
const util = require('util');
const fs = require('fs');
let readFile = util.promisify(fs.readFile)

// generator 生成器函数
function * read() {
  let data = yield readFile('./a.txt','utf8');
  data = yield readFile(data,'utf8');
  return data;
}

// 方法一:Generator
let it = read(); // 生成器函数返回一个 Iterator 迭代器对象
let {value:v1, done:d1} = it.next(); // 注意:第一个next传参是无效的
v1.then(data=>{
  console.log(data) // b.txt
  let {value:v2, done:d2} = it.next(data); // 将第一次的结果作为第二次的入参
  return v2
}).then(data=>{
  console.log(data) // 执行结果:c
})

// 方法二:使用 co 库
const co = require('co')
// co 方法:包装 generator 生成器函数,内部自动执行完成,返回一个 promise
co(read()).then(data=>{
  console.log(data);  // 执行结果:c
})

对比 Generator 和 co 两种方案:

  • 后者的实现,明显更加清晰和简洁:无需分步执行、获取中间结果、判断是否结束;
  • co 使 generator 能够自动执行完成,并返回 primise 形式的结果;

三,co 库的实现

手写一个 co 方法:主要使用了”异步迭代“的思想;

/**
 * 自动执行迭代器
 * @param {*} it generator执行返回的迭代器函数
 * @returns 
 */
function co(it) {
  return new Promise((resolve, reject) => {
    function next(data) { // 将上一次的结果作为下一次的入参
      let { value, done } = it.next(data);
      if (done) { // 迭代器执行完成,返回最终结果
        resolve(value);
      } else {
        // 备注:使用 Promise.resolve 将 value 包装成Promise
        // 成功:说明迭代器还没有执行完,继续递归调用 next,直至迭代器完成
        Promise.resolve(value).then(next, reject);// 注意此处的 next 传入的是当前 function
      }
    }

    next();// 第一次调用 next 时,传参是无效的
  })
}

四,结尾

本篇,主要介绍了 co 库的使用和实现原理,主要涉及以下几个点:

  • co 库的简介:特性、用法、功能分析;
  • co 库的实现和原理分析;

下一篇,继续介绍 async/await:generator 语法糖,嵌套回调问题的最终解决方案;