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

这两个是理解 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 遍历、能暂停/恢复的核心原因。

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

javascript 复制代码
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 帮我们自动实现了迭代器协议,我们手动写一个迭代器,就能明白它的核心逻辑:

kotlin 复制代码
// 手动实现一个"模拟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 自动执行):

javascript 复制代码
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」,我们手写一个简化版,吃透它的底层:

scss 复制代码
// 简化版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 不是只懂表面用法,而是理解底层设计。

相关推荐
AlienZHOU15 小时前
AI Coding 时代下,我的技术面试实践分享
前端·后端·面试
Captaincc18 小时前
AI用量v0.1.11更新发布 新增 jusage doctor 诊断指令 托盘展示token 和余额 新增 AutoClaw 支持
前端·后端·vibecoding
计算机魔术师19 小时前
德国Wiki被黑后两周,OpenAI终于把模型失控的账本摊开了
前端
kyriewen20 小时前
我让 AI 当面试官面了我一轮:第 3 个追问我就卡住了(附 10 道追问清单)
前端·面试·ai编程
IT_陈寒20 小时前
Python的GIL把我坑惨了,多线程跑得比单线程还慢
前端·人工智能·后端
前端snow20 小时前
ai agent --- 多agent框架之图编排引擎-langgraph
前端
竹林81820 小时前
OmniPic Studio v3.2.1 核心技术架构与全平台发版解析文档
前端·浏览器
JamesZhang8007820 小时前
页面内存只涨不跌? 一次泄漏排查, 牵出 WeakMap 的诞生
前端
Z小明21 小时前
第 6 章 组件进阶
前端·vue.js
江华森21 小时前
HTTP请求的完整过程详解:从DNS解析到TCP挥手的微秒级实战分析
前端