Asynchronous generator function
ECMAScript 6 introduced asynchronous generator functions, combining the features of generators and asynchronous functions, allowing asynchronous iteration logic to be written in a synchronous style. These functions are defined using the async function*
syntax and can produce an asynchronously iterable object, making them suitable for scenarios like streaming data or paginated requests.
Basic Syntax of Asynchronous Generator Functions
Asynchronous generator functions are declared with async function*
. Inside the function body, the yield
keyword can be used to pause execution and return a Promise. Each time the generator's next()
method is called, the function resumes execution from where it was paused until the next yield
or return
is encountered.
async function* asyncGenerator() {
yield await Promise.resolve(1);
yield await Promise.resolve(2);
return 3;
}
const gen = asyncGenerator();
gen.next().then(console.log); // { value: 1, done: false }
gen.next().then(console.log); // { value: 2, done: false }
gen.next().then(console.log); // { value: 3, done: true }
Iterating Over Asynchronous Generators
Asynchronous generators return an asynchronously iterable object, which can be iterated over using the for await...of
loop. This simplifies the handling of asynchronous data by eliminating the need to manually call next()
.
async function* fetchPages(urls) {
for (const url of urls) {
const response = await fetch(url);
yield response.json();
}
}
(async () => {
const urls = ['/api/page1', '/api/page2'];
for await (const data of fetchPages(urls)) {
console.log(data);
}
})();
Error Handling
Inside asynchronous generator functions, try...catch
can be used to catch errors, which can then be propagated via yield
or throw
. The caller can catch errors using the catch
method of the Promise returned by next()
.
async function* errorHandlingGenerator() {
try {
yield await Promise.resolve('Success');
throw new Error('Something went wrong');
} catch (error) {
yield `Caught error: ${error.message}`;
}
}
const gen = errorHandlingGenerator();
gen.next().then(console.log); // { value: 'Success', done: false }
gen.next().then(console.log); // { value: 'Caught error: Something went wrong', done: false }
Combining with yield*
The yield*
expression can delegate to another asynchronous generator, enabling the composition of generators. This allows the logic of multiple asynchronous generators to be chained together.
async function* generatorA() {
yield 'A1';
yield 'A2';
}
async function* generatorB() {
yield 'B1';
yield* generatorA();
yield 'B2';
}
(async () => {
for await (const value of generatorB()) {
console.log(value); // Output: B1, A1, A2, B2
}
})();
Practical Use Cases
Asynchronous generator functions are particularly well-suited for handling paginated data or real-time data streams. For example, fetching data page by page from a paginated API until no more data is available.
async function* paginatedFetch(url) {
let page = 1;
while (true) {
const response = await fetch(`${url}?page=${page}`);
const data = await response.json();
if (data.length === 0) break;
yield data;
page++;
}
}
(async () => {
const userDataStream = paginatedFetch('/api/users');
for await (const users of userDataStream) {
console.log('Fetched users:', users);
}
})();
Differences from Synchronous Generators
The main differences between asynchronous and synchronous generator functions are:
- Asynchronous generators are declared with
async function*
, while synchronous generators usefunction*
. - The
yield
in asynchronous generators returns a Promise, whereas in synchronous generators, it returns a direct value. - Asynchronous generators require iteration via
for await...of
, while synchronous generators usefor...of
.
// Synchronous generator
function* syncGenerator() {
yield 1;
yield 2;
}
// Asynchronous generator
async function* asyncGenerator() {
yield await Promise.resolve(1);
yield await Promise.resolve(2);
}
Performance Considerations
Asynchronous generator functions may introduce additional Promise overhead when handling large numbers of asynchronous operations. If asynchronous features are unnecessary, prefer synchronous generators. Additionally, avoid excessive nesting of await
in asynchronous generators to minimize unnecessary waiting.
// Not recommended: Excessive nesting of await
async function* inefficientGenerator() {
for (const item of list) {
const result1 = await step1(item);
const result2 = await step2(result1);
yield result2;
}
}
// Recommended: Parallel processing
async function* efficientGenerator() {
const promises = list.map(async (item) => {
const result1 = await step1(item);
return await step2(result1);
});
for (const promise of promises) {
yield await promise;
}
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:生成器与迭代器的关系
下一篇:内置可迭代对象