likes
comments
collection
share

JavaScript中的生成器函数以及yield

作者站长头像
站长
· 阅读数 35

生成器函数与yield语句

生成器函数是 JavaScript 中一种特殊类型的函数,它能够逐步产生多个值,而不是一次性返回全部结果。与普通函数不同,生成器函数可以暂停其执行过程,并在需要时恢复执行。

生成器函数使用 function* 声明语法来定义,而不是普通函数的 function 关键字。在生成器函数中,使用 yield 语句来指示函数的执行流程,yield 语句可以将函数的执行暂停,并返回一个值。

下面是一个简单的生成器函数的示例,它用于产生数字序列:

function* numberSequence() {
  let i = 1;
  while (true) {
    yield i++;
  }
}

const generator = numberSequence();
console.log(generator.next().value); // 1
console.log(generator.next().value); // 2
console.log(generator.next().value); // 3
console.log(generator.next().value); // 4

在这个示例中,我们定义了一个生成器函数 numberSequence(),它在一个无限循环中使用 yield 语句逐步产生数字序列。我们创建了一个生成器对象 generator 并对其调用了 next() 方法,每次调用 next() 方法都会从上一次 yield 语句处开始执行,并在下一个 yield 语句处暂停。

生成器函数有以下几个特点:

  1. 生成器函数使用 function* 声明语法来定义,而不是普通函数的 function 关键字。
  2. 生成器函数内部可以使用 yield 语句来暂停函数的执行,并返回一个值。
  3. 生成器函数可以多次返回值,每次返回一个值。
  4. 生成器函数可以使用 return 语句来结束函数的执行,并返回一个最终值。
  5. 生成器函数可以使用 throw 语句来抛出一个异常。
  6. 生成器函数可以通过 for...of 循环来迭代产生的值。

使用场景

生成器函数可以在以下场景中使用:

  1. 异步编程:使用生成器函数可以方便地实现异步编程模式,如协程和生成器模式。
  2. 处理大量数据:使用生成器函数可以避免一次性将大量数据全部加载到内存中,而是逐步从外部数据源获取数据。
  3. 状态机:使用生成器函数可以轻松地实现状态机,例如有限状态机(FSM)或下推自动机(PDA)。

总之,生成器函数是 JavaScript 中一种非常有用的函数类型,它可以使程序更加灵活、可扩展和易于理解。它使得函数能够产生多个值,并可以控制函数的执行过程,让函数更具有交互性和可控性。

使用场景实现代码

  1. 异步编程:使用生成器函数实现协程

生成器函数通常用于异步编程,特别是实现协程(coroutine)时非常有用。在异步编程中,程序可以在等待某些操作完成时执行其他任务,以提高性能和响应能力。

以下是一个简单的例子,演示了如何使用生成器函数来实现协程,它可以在等待异步操作完成时暂停执行,并在操作完成时恢复执行:

function* myCoroutine() {
  const result1 = yield asyncOperation1();
  const result2 = yield asyncOperation2(result1);
  const result3 = yield asyncOperation3(result2);
  return result3;
}

const generator = myCoroutine();
generator.next().value.then(result1 => {
  return generator.next(result1).value;
}).then(result2 => {
  return generator.next(result2).value;
}).then(result3 => {
  console.log(result3);
});

在这个示例中,我们定义了一个协程 myCoroutine(),它执行了一系列异步操作,并在每次等待异步操作完成时暂停执行,并使用 yield 语句返回异步操作的结果。我们创建了一个生成器对象 generator 并对其调用了 next() 方法,每次调用 next() 方法都会从上一次 yield 语句处开始执行,并在下一个 yield 语句处暂停。我们使用 Promise 的 then() 方法来处理异步操作的结果,并将结果传递给下一个 yield 语句。

使用生成器函数实现协程的好处在于,它可以使异步编程更加容易和直观。使用生成器函数,我们可以将异步操作组织成顺序代码,而不需要使用回调函数或 Promise 链式调用等复杂的编程模式。

  1. 处理大量数据:逐步读取大型数据

生成器函数也非常适合处理大量数据。使用生成器函数,我们可以逐步从外部数据源获取数据,而不是一次性将大量数据全部加载到内存中。这样可以提高程序的效率和可扩展性。

以下是一个简单的例子,演示了如何使用生成器函数从文件中逐行读取数据:

function* readLines(file) {
  const stream = fs.createReadStream(file);
  const reader = readline.createInterface({
    input: stream,
    crlfDelay: Infinity
  });

  for await (const line of reader) {
    yield line;
  }
}

const lineGenerator = readLines('large-file.txt');
for (let line of lineGenerator) {
  console.log(line);
}

在这个示例中,我们定义了一个生成器函数 readLines(),它从文件中逐行读取数据,并使用 yield 语句将每行数据返回。我们创建了一个生成器对象 lineGenerator 并对其进行迭代,每次迭代都会获取一个新的数据行并进行处理。这种方式可以避免一次性将大量数据全部加载到内内存中,而是逐步从外部数据源获取数据,从而提高程序的效率和可扩展性。

  1. 无限序列:生成无限序列

生成器函数还可以用于生成无限序列。例如,我们可以使用生成器函数来生成斐波那契数列(Fibonacci sequence)

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

const generator = fibonacci();
for (let i = 0; i < 10; i++) {
  console.log(generator.next().value);
}

在这个示例中,我们定义了一个生成器函数 fibonacci(),它使用一个 while 循环不断生成斐波那契数列,并使用 yield 语句将每个数返回。我们创建了一个生成器对象 generator 并对其进行迭代,每次迭代都会获取一个新的数并进行处理。由于生成器函数可以无限生成序列,因此我们需要在迭代时指定要生成的元素数量。

使用生成器函数生成无限序列的好处在于,它可以使我们更加方便地生成任意长度的序列,并可以避免一次性生成大量的数据。

yield和async await对比

yieldasync/await 都是 JavaScript 中的异步编程技术,它们都可以用于协程(coroutine)的实现,但是它们的使用方式和实现方式略有不同。

yield 用于定义生成器函数中的迭代器对象,通过 yield 语句可以将控制权交回给生成器的调用者,以便生成器可以在需要时暂停执行。生成器可以在需要时恢复执行,并继续执行其余的代码。yield 主要用于生成器函数中的同步代码,它可以将同步代码转换为异步代码,从而实现协程。

async/await 是 ECMAScript 2017 标准中引入的一种异步编程模式,它基于 Promise 对象并使用关键字 asyncawait 来进行异步代码的编写和执行。async 关键字用于定义一个异步函数,它会返回一个 Promise 对象,而 await 关键字用于等待一个异步操作的完成,从而避免了回调函数的嵌套和代码可读性的问题。

相比于 yieldasync/await 更加直观、易于理解和使用。使用 async/await 可以使代码更加简洁、清晰,并且可以更好地处理异步代码的错误和异常。但是,与 yield 相比,async/await 的实现更加复杂,并且需要在编译时将异步代码转换为 Promise 对象,因此在一些旧版浏览器中可能不被支持。

总体来说,yieldasync/await 都是实现异步编程的有效方法,它们各有优劣,可以根据具体情况选择使用。如果需要实现较为复杂的异步操作,建议使用 async/await;如果需要实现一些简单的异步操作,可以使用 yield 进行实现。

转载自:https://juejin.cn/post/7207250137466552375
评论
请登录