📜  asyncGenerator - Javascript (1)

📅  最后修改于: 2023-12-03 14:39:23.315000             🧑  作者: Mango

asyncGenerator - Javascript

在Javascript语言中,asyncGenerator被广泛用于异步编程,尤其在ES6版本后。它是生成器函数和async/await的结合,提供了一种方便遍历异步流程的方式。

基本语法

声明一个async generator,需要在函数前面加上async和*,像这样:

async function* myAsyncGenerator() {
  // 生成器逻辑代码
}

在async generator中,可以使用yield语句暂停函数的执行,并返回一个Promise对象,用于异步操作。

下面是一个简单的例子,我们将一个数组中的元素转换成大写,并通过setTimeout模拟异步操作:

async function* toUpperAsyncGenerator(array) {
  for (let i = 0; i < array.length; i++) {
    yield await new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(array[i].toUpperCase());
      }, 1000);
    });
  }
}

const myArray = ["apple", "banana", "orange"];
const myAsyncGenerator = toUpperAsyncGenerator(myArray);

(async () => {
  for await (let value of myAsyncGenerator) {
    console.log(value);
  }
})();

在这个例子中,我们用async generator将数组中的元素转换成大写。注意,在for await...of循环中,我们使用了await关键字来等待Promise对象的解决。

async generator的return()

和普通的生成器函数一样,async generator也可以使用return()来结束它的执行。

假设我们有一个生成斐波那契数列的async generator:

async function* fibAsyncGenerator() {
  let a = 0, b = 1;
  while (true) {
    const nextNumber = await new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(a);
      }, 1000);
    });
    yield nextNumber;
    [a, b] = [b, a + b];
  }
}

如果我们只想输出前5个斐波那契数列的数字,可以使用return():

(async () => {
  const myAsyncGenerator = fibAsyncGenerator();
  for (let i = 0; i < 5; i++) {
    console.log(await myAsyncGenerator.next());
  }
  myAsyncGenerator.return();
})();

执行return()后,async generator不会再生成新的值,而是直接退出。

async generator的throw()

和普通的生成器函数一样,async generator也可以使用throw()来抛出异常。

假设我们有一个async generator,它每隔1秒钟生成一个随机数,如果生成的数小于0.5,就会抛出一个异常:

async function* myAsyncGenerator() {
  while (true) {
    const randomNumber = Math.random();
    yield await new Promise((resolve, reject) => {
      setTimeout(() => {
        if (randomNumber < 0.5) {
          reject(new Error("Random number is too small!"));
        } else {
          resolve(randomNumber);
        }
      }, 1000);
    });
  }
}

在for await...of循环中,我们可以使用try...catch语句来捕捉异常:

(async () => {
  const myAsyncGenerator = myAsyncGenerator();
  try {
    for await (let value of myAsyncGenerator) {
      console.log(value);
    }
  } catch (e) {
    console.error(e);
    myAsyncGenerator.throw(e);
  }
})();

如果async generator内部抛出了异常,程序会执行catch语句块,我们可以在其中处理异常,并调用throw()来再次抛出异常,使程序继续往外层传递。

async generator的return()和throw()的区别

async generator的return()和throw()都会结束它的执行,但它们的执行顺序有所不同。

在执行return()后,async generator会将done属性设置为true,并且不再向下执行。但如果你想在return()之后再执行一些逻辑代码,可以在async generator函数后面加上finally关键字,像这样:

async function* myAsyncGenerator() {
  try {
    while (true) {
      // 遍历异步流程
    }
  } finally {
    console.log("Async generator has been closed.");
  }
}

在上面的代码中,无论是通过return()还是throw()结束async generator的执行,finally语句块都会被执行。

而在执行throw()后,async generator就会抛出一个异常,并且中止它的执行。如果你没有在async generator中捕捉这个异常,它将会向上层函数传递,直到有一个函数处理它为止。

async generator的应用场景

async generator用于遍历异步流程的场景非常多,比如,在Node.js中读取一个大型文件的过程中,可以使用async generator逐行读取文件中的数据,如下所示:

const readline = require("readline");
const fs = require("fs");

async function* readLinesAsyncGenerator(filename) {
  const fileStream = fs.createReadStream(filename);
  const rl = readline.createInterface({ input: fileStream, crlfDelay: Infinity });
  for await (let line of rl) {
    yield line;
  }
}

(async () => {
  const myAsyncGenerator = readLinesAsyncGenerator("./example.txt");
  for await (let line of myAsyncGenerator) {
    console.log(line);
  }
})();

这个例子中,我们使用了Node.js的readline模块和fs模块,创建了一个async generator,它可以逐行读取一个文本文件的数据,并且按照我们期望的方式进行异步处理。

总的来说,async generator是一种非常有用的异步编程方法,它可以遍历异步流程,让我们在处理大量异步任务的时候更加方便和高效。