普通视图

发现新文章,点击刷新页面。
今天 — 2026年3月8日首页

Generator 迭代器协议 & co 库底层原理+实战

2026年3月8日 12:20

这两个是理解 Generator 的「关键底层知识点」,也是面试中容易被追问的细节——迭代器协议是 Generator 能被遍历的基础,co 库是 Generator 实现异步流程控制的核心,我用最通俗的语言+代码拆解清楚。

一、迭代器协议(Iterator Protocol):Generator 能“暂停/遍历”的底层规则

1. 先搞懂:什么是“协议”?

协议就是「约定好的规则」——ES6 规定了两套和遍历相关的协议:

  • 可迭代协议(Iterable Protocol) :一个对象只要有 [Symbol.iterator]() 方法,且该方法返回一个「迭代器对象」,就称这个对象“可迭代”(比如 Array、Set、Map、Generator 对象都符合);
  • 迭代器协议(Iterator Protocol) :一个对象只要有 next() 方法,且 next() 返回 { value: 产出值, done: 是否完成 } 格式的对象,就称这个对象是“迭代器”。

2. Generator 与迭代器协议的关系

Generator 函数调用后返回的「生成器对象」,同时满足可迭代协议 + 迭代器协议——这是它能被 for...of 遍历、能暂停/恢复的核心原因。

验证:生成器对象的协议合规性

function* gen() {
  yield 1;
  yield 2;
}
const g = gen(); // 生成器对象

// 1. 验证迭代器协议:有next(),返回{value, done}
console.log(g.next()); // { value: 1, done: false }
console.log(g.next()); // { value: 2, done: false }
console.log(g.next()); // { value: undefined, done: true }

// 2. 验证可迭代协议:有[Symbol.iterator](),且返回自身(迭代器)
console.log(typeof g[Symbol.iterator] === 'function'); // true
console.log(g[Symbol.iterator]() === g); // true(关键:返回自身)

// 3. 因此能被for...of遍历(for...of只遍历done: false的value,忽略return值)
for (let val of g) {
  console.log(val); // 1、2(return的值不会被遍历)
}

3. 手动实现迭代器协议(理解 Generator 底层)

Generator 本质是 ES6 帮我们自动实现了迭代器协议,我们手动写一个迭代器,就能明白它的核心逻辑:

// 手动实现一个“模拟Generator”的迭代器
const myIterator = {
  _step: 0, // 记录执行步骤
  next() {
    this._step++;
    if (this._step === 1) {
      return { value: 1, done: false }; // 对应yield 1
    } else if (this._step === 2) {
      return { value: 2, done: false }; // 对应yield 2
    } else {
      return { value: 3, done: true }; // 对应return 3
    }
  },
  // 实现可迭代协议:返回自身
  [Symbol.iterator]() {
    return this;
  }
};

// 调用方式和Generator完全一致
console.log(myIterator.next()); // { value: 1, done: false }
console.log(myIterator.next()); // { value: 2, done: false }
console.log(myIterator.next()); // { value: 3, done: true }

4. 面试高频:迭代器协议的核心考点

  • Q:为什么 Generator 对象能被 for...of 遍历?
    A:因为 Generator 对象符合「可迭代协议」——有 [Symbol.iterator]() 方法且返回迭代器(自身),而 for...of 会自动调用迭代器的 next() 直到 done: true
  • Q:迭代器协议和可迭代协议的区别?
    A:迭代器协议是“对象有 next() 且返回 {value, done}”,可迭代协议是“对象有 Symbol.iterator 且返回迭代器”;前者是“能一步步取值”,后者是“能被遍历”。

二、co 库:Generator 异步流程的“自动执行器”

1. co 库的核心作用

在 async/await 出现前,Generator 处理异步的最大痛点是「需要手动调用 next()」——co 库的本质是一个自动执行器:它能自动调用 Generator 迭代器的 next(),并把异步操作(Promise)的结果作为参数传入下一个 next(),直到 Generator 执行完毕。

简单说:co 库 = 自动调用 next() + 处理 Promise 结果 + 异常捕获

2. co 库的基本使用(先看效果)

先安装 co 库:

npm install co

使用示例(对比手动执行和 co 自动执行):

const co = require('co');

// 模拟异步请求
function fetchData(url) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(`数据:${url}`), 1000);
  });
}

// Generator异步函数
function* gen() {
  const res1 = yield fetchData('https://api1.com');
  const res2 = yield fetchData(`https://api2.com?data=${res1}`);
  return res2;
}

// 方式1:手动执行(繁琐)
const g = gen();
g.next().value.then(res1 => {
  g.next(res1).value.then(res2 => {
    console.log(g.next(res2).value); // 数据:https://api2.com?data=数据:https://api1.com
  });
});

// 方式2:co自动执行(简洁)
co(gen).then(res => {
  console.log(res); // 数据:https://api2.com?data=数据:https://api1.com
});

3. co 库的核心原理(手写简化版)

co 库的源码不到 200 行,核心逻辑是「递归调用 next() + 处理 Promise」,我们手写一个简化版,吃透它的底层:

// 简化版co库:自动执行Generator
function co(genFunc) {
  // 返回Promise,符合现代异步规范
  return new Promise((resolve, reject) => {
    const g = genFunc(); // 获取生成器迭代器

    // 递归执行next的函数
    function next(val) {
      let result;
      try {
        result = g.next(val); // 恢复执行,传入上一次异步结果
      } catch (e) {
        return reject(e); // 捕获Generator内部异常
      }

      const { value, done } = result;
      if (done) {
        // 执行完毕,resolve最终值
        return resolve(value);
      }

      // 核心:如果value是Promise,等待resolve后继续next
      // 非Promise则直接传入下一个next
      Promise.resolve(value).then(
        (data) => next(data), // 异步成功:把结果传入下一个next
        (err) => g.throw(err) // 异步失败:向Generator内部抛异常
      );
    }

    // 启动执行器
    next();
  });
}

// 测试:和官方co库效果一致
co(gen).then(res => console.log(res)); // 数据:https://api2.com?data=数据:https://api1.com

4. co 库的核心规则(面试必知)

co 库能自动执行的前提是:Generator 中 yield 后面的值必须是以下类型之一(否则会直接传入 next()):

  1. Promise(最常用);
  2. 可迭代对象(Array、Set、Generator 对象等);
  3. 普通对象/函数(co 会尝试转换为 Promise)。

5. co 库 vs async/await(核心关系)

ES2017 引入的 async/await,本质是「Generator + co 库」的语法糖——浏览器/Node 内置了类似 co 的自动执行器,无需手动引入库。

维度 co + Generator async/await
执行方式 手动引入co库 语言原生支持,自动执行
异常处理 需配合try/catch + co的catch 原生try/catch即可
返回值 Promise(co返回) Promise(async函数返回)
语法简洁性 稍繁琐(function* + yield) 更简洁(async + await)
兼容性 需ES6环境 + co库 需ES2017环境(或babel转译)

6. 面试高频:co 库的考点

  • Q:co 库的作用是什么?
    A:co 库是 Generator 函数的自动执行器,核心解决 Generator 处理异步时需要手动调用 next() 的问题;它会自动递归调用 next(),并将 yield 后 Promise 的结果传入下一个 next(),最终返回一个 Promise,简化 Generator 异步流程控制。
  • Q:async/await 是不是替代了 co 库?
    A:是的。async/await 是 Generator + 自动执行器的语法糖,浏览器/Node 内置了类似 co 的执行逻辑,因此在现代开发中,co 库已几乎被 async/await 取代;但理解 co 库的原理,能更好地理解 async/await 的底层逻辑。

总结

迭代器协议核心

  1. 迭代器协议:对象有 next() 且返回 { value, done };可迭代协议:对象有 [Symbol.iterator]() 且返回迭代器;
  2. Generator 对象同时满足两套协议,因此能被 for...of 遍历、能暂停/恢复;
  3. 迭代器协议是 Generator 实现暂停/遍历的底层规则。

co 库核心

  1. co 库是 Generator 的自动执行器,核心逻辑是「递归调用 next() + 处理 Promise 结果」;
  2. co 库解决了 Generator 手动调用 next() 的痛点,是 async/await 的“前身”;
  3. 现代开发中 async/await 已替代 co 库,但理解 co 库原理是掌握 async/await 底层的关键。

这两个知识点是 Generator 进阶的核心,面试中只要能讲清「迭代器协议的规则」和「co 库的自动执行逻辑」,就能体现你对 Generator 不是只懂表面用法,而是理解底层设计。

❌
❌