Skip to content

Generator 函数

ES6 引入的生成器函数,可以暂停和恢复函数执行,返回一个迭代器对象(Iterator),通常与 Promise 结合使用

案例

基础语法

js
// 1. 定义生成器函数
function* gen() {
  yield 1;
  yield 2;
  yield 3;
}

// 2. 调用函数不会立即运行,只返回迭代器
const g = gen();

// 3. 真正执行靠 .next()
console.log(g.next()); // { value: 1, done: false }
console.log(g.next()); // { value: 2, done: false }
console.log(g.next()); // { value: 3, done: false }
console.log(g.next()); // { value: undefined, done: true }

给 yield 传参

js
function* gen() {
  const a = yield 'first';
  const b = yield a;
  yield a + b;
}

const g = gen();
g.next();       // {value: 'first'}
g.next(10);     // a = 10
g.next(20);     // b = 20
g.next();       // value: 30

遍历 Generator 的方法

js
function* gen() {
  yield 1;
  yield 2;
  yield 3;
}

// for of 遍历
for (const v of gen()) {
  console.log(v); // 1 2 3
}

// 展开运算符 ...
const arr = [...gen()]; // [1,2,3]

// Array.from
const arrFrom = Array.from(gen()); // [1,2,3]

// 解构赋值
const [a, b, c] = gen();

yield* 语法(委托生成器)

在一个 Generator 里执行另一个 Generator

js
function* gen1() {
  yield 1;
  yield 2;
}

function* gen2() {
  yield* gen1();
  yield 3;
}

[...gen2()]; // [1,2,3]

终止 yield

js
// return 提前结束
function* gen() {
  yield 1;
  yield 2;
  yield 3;
}
const g = gen();
g.next();    // 1
g.return(99);
g.next();    // done: true

// throw 捕获异常
function* gen() {
  try {
    yield 1;
  } catch (err) {
    console.log('捕获', err);
  }
}

const g = gen();
g.next();
g.throw('错误');

异步流程控制

js
function fetchUser() {
  return new Promise(resolve => {
    setTimeout(() => resolve({ id: 1 }), 1000);
  });
}

function* gen() {
  const user = yield fetchUser();
  console.log(user);
}

// 执行
const g = gen();
g.next().value.then(data => {
  g.next(data);
});

自动执行器

思想:递归反复调用 next 函数,直到res.done === true 结束递归

shell
# 参数为生成器函数,
function run(genFunc) {
  return new Promise((resolve, reject) => {
    const it = genFunc(); # 创建迭代器(还没开始遍历)

    # 递归遍历函数
    function next(arg) {
      try {
        const res = it.next(arg);  # 核心:每执行一次,就往下走一个 yield

        # 遍历结束
        if (res.done) {
          return resolve(res.value);
        }

        # 还没结束
        Promise.resolve(res.value)
          .then(next) # 递归:异步完成后,继续递归遍历下一个 next
          .catch(reject);
      } catch (err) {
        reject(err);
      }
    }

    next(); # 启动第一次遍历
  });
}

进阶练习

shell
# 1. Generator 是什么?
可以暂停和恢复的函数,用 function* yield,返回迭代器,用于异步流程控制。

# 2. next () 做什么?
执行到下一个 yield,返回 { value, done }。

# 3. yield* 和 yield 区别?
yield 返回值;yield* 委托另一个生成器。

# 4. Generator 怎么遍历?
for...of、... 展开、Array.from。

# 5. async/await 和 Generator 关系?
async/await 是基于 Generator Promise 实现的语法糖,内置自动执行器。

# 6. Generator 优点?
逻辑同步化、可暂停、可控性强。

# 7. Generator 与 async/await 的关系
async/await 就是 Generator + Promise 的语法糖
async function function*
await yield
自动执行 内置 co 逻辑

所以可以理解为:
async function fn() {
  const data = await fetch();
}
本质就是:
function* fn() {
  const data = yield fetch();
}
加一个自动执行器。