深入理解 JavaScript 中的 async function*
、function*
和 async function
在现代 JavaScript 编程中,异步操作和生成器函数是两个非常重要的概念。它们可以帮助我们更有效地处理异步流程和可迭代的数据序列。本文将深入探讨 function*
(生成器函数)、async function
(异步函数)以及它们的结合——async function*
(异步生成器函数),并重点详细说明 async function*
和 function*
的区别。
目录
生成器函数(function*
)
什么是生成器函数
生成器函数是 ES6 引入的一种特殊函数,使用 function*
声明。它可以在执行过程中暂停和恢复,通过 yield
关键字产生一系列值。
特点:
- 可暂停执行:生成器函数可以在
yield
表达式处暂停。 - 返回一个迭代器对象:调用生成器函数会返回一个迭代器,可用于遍历生成的值。
生成器函数的用法
基本语法:
function* generatorFunction() {
// 生成器函数主体
}
示例:
function* numberGenerator() {
yield 1;
yield 2;
yield 3;
}
const gen = numberGenerator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }
说明:
- 每次调用
next()
,生成器函数会从上一次暂停的地方继续执行,直到遇到下一个yield
。
异步函数(async function
)
什么是异步函数
异步函数是 ES2017 引入的一种语法,用于简化基于 Promise 的异步操作。使用 async
关键字声明,内部可以使用 await
等待一个 Promise 的结果。
特点:
- 简化异步代码:使异步代码看起来像同步代码,提高可读性。
- 返回 Promise:异步函数始终返回一个 Promise。
异步函数的用法
基本语法:
async function asyncFunction() {
// 异步函数主体
}
示例:
async function fetchData() {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
}
fetchData().then(data => {
console.log(data);
});
说明:
- 使用
await
等待异步操作完成,获取结果。 - 异步函数返回的 Promise,可以使用
.then()
或await
进行处理。
异步生成器函数(async function*
)
什么是异步生成器函数
异步生成器函数结合了生成器函数和异步函数的特性。使用 async function*
声明,可以在生成器函数中使用 await
,并异步地 yield
值。
特点:
- 异步迭代:可用于处理异步数据流。
- 返回异步迭代器:生成的迭代器对象符合异步迭代器协议,可用于
for await...of
循环。
异步生成器函数的用法
基本语法:
async function* asyncGeneratorFunction() {
// 异步生成器函数主体
}
示例:
async function* asyncNumberGenerator() {
let i = 0;
while (i < 3) {
await new Promise(resolve => setTimeout(resolve, 1000)); // 模拟异步操作
yield i++;
}
}
(async () => {
for await (let num of asyncNumberGenerator()) {
console.log(num); // 每秒输出一个数字:0, 1, 2
}
})();
说明:
- 使用
for await...of
循环遍历异步生成器函数返回的异步迭代器。 - 可以在函数内部使用
await
等待异步操作,然后yield
结果。
async function*
和 function*
的区别
返回值的区别
-
function*
返回同步迭代器调用
function*
声明的生成器函数,会返回一个同步迭代器对象。该迭代器符合可迭代协议,可用于for...of
循环。function* generatorFunction() { yield 1; yield 2; } const iterator = generatorFunction(); console.log(iterator.next()); // { value: 1, done: false }
-
async function*
返回异步迭代器调用
async function*
声明的异步生成器函数,会返回一个异步迭代器对象。该迭代器符合异步可迭代协议,需要使用for await...of
循环遍历。async function* asyncGeneratorFunction() { yield 1; yield 2; } const asyncIterator = asyncGeneratorFunction(); console.log(await asyncIterator.next()); // { value: 1, done: false }
处理异步操作的能力
-
function*
不能直接处理异步操作在
function*
中,如果需要处理异步操作,不能直接使用await
。需要使用额外的逻辑,如返回 Promise 或通过外部控制。function* generatorFunction() { const data = yield fetchData(); // 无法直接使用 await }
示例:
function* fetchGenerator() { const dataPromise = fetch('https://api.example.com/data'); const data = yield dataPromise; console.log(data); } const gen = fetchGenerator(); const dataPromise = gen.next().value; dataPromise.then(response => response.json()) .then(data => gen.next(data));
-
async function*
可以直接使用await
在
async function*
中,可以直接使用await
等待异步操作完成,然后再yield
结果。async function* asyncGeneratorFunction() { const data = await fetchData(); yield data; }
示例:
async function* fetchAsyncGenerator() { const response = await fetch('https://api.example.com/data'); const data = await response.json(); yield data; } (async () => { for await (let data of fetchAsyncGenerator()) { console.log(data); } })();
迭代方式的不同
-
function*
使用同步迭代使用
for...of
循环遍历生成器函数返回的迭代器。function* generatorFunction() { yield 1; yield 2; } for (let value of generatorFunction()) { console.log(value); // 输出 1 和 2 }
-
async function*
使用异步迭代使用
for await...of
循环遍历异步生成器函数返回的异步迭代器。async function* asyncGeneratorFunction() { yield 1; yield 2; } (async () => { for await (let value of asyncGeneratorFunction()) { console.log(value); // 输出 1 和 2 } })();
应用场景的差异
-
function*
适用于同步数据序列当需要生成一系列同步的、可迭代的数据时,使用生成器函数是非常方便的选择。
示例:
function* idGenerator() { let id = 0; while (true) { yield id++; } } const gen = idGenerator(); console.log(gen.next().value); // 输出 0 console.log(gen.next().value); // 输出 1
-
async function*
适用于异步数据流当需要逐步获取异步数据(如网络请求、文件读取)并以可迭代的方式处理时,异步生成器函数是理想的工具。
示例:
async function* readLines(file) { let reader = file.getReader(); let decoder = new TextDecoder('utf-8'); let { done, value } = await reader.read(); while (!done) { yield decoder.decode(value); ({ done, value } = await reader.read()); } } (async () => { for await (let line of readLines(someFile)) { console.log(line); } })();
总结
- 生成器函数(
function*
):适用于需要自定义迭代行为的同步场景。不能直接处理异步操作,返回同步迭代器。 - 异步函数(
async function
):简化异步操作,提高代码的可读性和可维护性。返回 Promise,不适用于需要迭代的场景。 - 异步生成器函数(
async function*
):结合了生成器和异步函数的特性,适用于处理异步数据流。可以直接使用await
,返回异步迭代器。