📅  最后修改于: 2023-12-03 14:39:23.315000             🧑  作者: Mango
在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:
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,它每隔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()都会结束它的执行,但它们的执行顺序有所不同。
在执行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用于遍历异步流程的场景非常多,比如,在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是一种非常有用的异步编程方法,它可以遍历异步流程,让我们在处理大量异步任务的时候更加方便和高效。