9.3 生成器函数(function*)与 yield

9.3 生成器函数(function*)与 yield

生成器函数(Generator Function)是 ES6 引入的一种特殊函数,用于生成一个迭代器。通过 function*yield 关键字,可以更简洁地实现自定义迭代器。本节将详细介绍生成器函数的用法及其应用场景。


9.3.1 生成器函数的基本概念

生成器函数使用 function* 定义,内部通过 yield 关键字产生值。调用生成器函数时,返回一个生成器对象,该对象符合迭代器协议。

1. 定义生成器函数

function* generatorFunction() {
  yield "First";
  yield "Second";
  yield "Third";
}

2. 生成器对象
生成器函数返回的生成器对象是一个迭代器,可以通过 next() 方法获取值:

const generator = generatorFunction();

console.log(generator.next()); // 输出 { value: "First", done: false }
console.log(generator.next()); // 输出 { value: "Second", done: false }
console.log(generator.next()); // 输出 { value: "Third", done: false }
console.log(generator.next()); // 输出 { value: undefined, done: true }

9.3.2 yield 关键字

yield 关键字用于暂停生成器函数的执行,并返回一个值。每次调用 next() 方法时,生成器函数会从上次暂停的位置继续执行。

1. 基本用法

function* generatorFunction() {
  yield 1;
  yield 2;
  yield 3;
}

const generator = generatorFunction();

for (const value of generator) {
  console.log(value); // 输出 1, 2, 3
}

2. 传递值
next() 方法可以传递一个值,该值会作为 yield 表达式的返回值:

function* generatorFunction() {
  const a = yield "First";
  const b = yield "Second";
  yield a + b;
}

const generator = generatorFunction();

console.log(generator.next()); // 输出 { value: "First", done: false }
console.log(generator.next(2)); // 输出 { value: "Second", done: false }
console.log(generator.next(3)); // 输出 { value: 5, done: false }
console.log(generator.next()); // 输出 { value: undefined, done: true }

9.3.3 生成器函数的应用场景

1. 自定义迭代器
生成器函数可以简化自定义迭代器的实现:

function* range(start, end) {
  for (let i = start; i <= end; i++) {
    yield i;
  }
}

for (const value of range(1, 3)) {
  console.log(value); // 输出 1, 2, 3
}

2. 惰性求值
生成器函数可以实现惰性求值,只在需要时计算值:

function* fibonacci() {
  let prev = 0,
    curr = 1;
  while (true) {
    yield curr;
    [prev, curr] = [curr, prev + curr];
  }
}

const fib = fibonacci();
console.log(fib.next().value); // 输出 1
console.log(fib.next().value); // 输出 1
console.log(fib.next().value); // 输出 2
console.log(fib.next().value); // 输出 3

3. 异步编程
生成器函数可以与 Promise 结合,实现更简洁的异步编程:

function* asyncGenerator() {
  const result1 = yield fetch("https://api.example.com/data1");
  const result2 = yield fetch("https://api.example.com/data2");
  return [result1, result2];
}

function runAsyncGenerator(generator) {
  const iterator = generator();

  function handle(iteratorResult) {
    if (iteratorResult.done) {
      return iteratorResult.value;
    }

    const promise = iteratorResult.value;
    return promise.then((res) => handle(iterator.next(res)));
  }

  return handle(iterator.next());
}

runAsyncGenerator(asyncGenerator).then((results) => {
  console.log(results);
});

9.3.4 示例代码

示例 1:自定义迭代器

function* range(start, end) {
  for (let i = start; i <= end; i++) {
    yield i;
  }
}

for (const value of range(1, 3)) {
  console.log(value); // 输出 1, 2, 3
}

示例 2:惰性求值

function* fibonacci() {
  let prev = 0,
    curr = 1;
  while (true) {
    yield curr;
    [prev, curr] = [curr, prev + curr];
  }
}

const fib = fibonacci();
console.log(fib.next().value); // 输出 1
console.log(fib.next().value); // 输出 1
console.log(fib.next().value); // 输出 2
console.log(fib.next().value); // 输出 3

示例 3:异步编程

function* asyncGenerator() {
  const result1 = yield fetch("https://api.example.com/data1");
  const result2 = yield fetch("https://api.example.com/data2");
  return [result1, result2];
}

function runAsyncGenerator(generator) {
  const iterator = generator();

  function handle(iteratorResult) {
    if (iteratorResult.done) {
      return iteratorResult.value;
    }

    const promise = iteratorResult.value;
    return promise.then((res) => handle(iterator.next(res)));
  }

  return handle(iterator.next());
}

runAsyncGenerator(asyncGenerator).then((results) => {
  console.log(results);
});

9.3.5 总结

  • 生成器函数:使用 function* 定义,通过 yield 产生值。
  • 生成器对象:符合迭代器协议,可以通过 next() 方法获取值。
  • 应用场景:自定义迭代器、惰性求值、异步编程。

通过掌握生成器函数和 yield 关键字,你可以编写更简洁、更灵活的代码。在接下来的学习中,我们将继续探索 JavaScript 的其他高级特性。


思考题

  1. 生成器函数与普通函数的主要区别是什么?
  2. 如何通过生成器函数实现惰性求值?
  3. 在什么情况下应该使用生成器函数处理异步操作?
#前端开发 分享于 2025-03-21

【 内容由 AI 共享,不代表本站观点,请谨慎参考 】