阿里云主机折上折
  • 微信号
Current Site:Index > async generator function

async generator function

Author:Chuan Chen 阅读数:14577人阅读 分类: JavaScript

Basic Concepts of ECMAScript 6 Async Generator Functions

Async generator functions combine the features of async functions and generator functions in ES6, allowing developers to write asynchronous code in a synchronous style. These functions are declared using the async function* syntax and can use the yield keyword to pause execution while also handling asynchronous operations with await.

async function* asyncGenerator() {
  yield await Promise.resolve(1);
  yield await Promise.resolve(2);
  yield await Promise.resolve(3);
}

Syntax Structure of Async Generator Functions

The syntax of async generator functions is similar to that of regular generator functions, but with the async modifier added before the function keyword:

async function* name([param[, param[, ...param]]]) {
  // Function body
}

This type of function returns an async generator object, which implements both the async iteration protocol and the iterator protocol. Each call to the next() method returns a Promise that resolves to an object with value and done properties.

How Async Generator Functions Work

When an async generator function is called, it does not immediately execute the function body. Instead, it returns an async generator object. The function body only begins execution when the next() method of this object is called, and it runs until the first yield expression is encountered:

async function* fetchInSequence(urls) {
  for (const url of urls) {
    const response = await fetch(url);
    yield response.json();
  }
}

const generator = fetchInSequence(['url1', 'url2', 'url3']);
generator.next().then(result => console.log(result));

Using for await...of Loops with Async Generators

The for await...of loop is a syntax specifically designed for asynchronous iteration, making it easy to traverse values produced by async generators:

async function* generateSequence(start, end) {
  for (let i = start; i <= end; i++) {
    await new Promise(resolve => setTimeout(resolve, 100));
    yield i;
  }
}

(async () => {
  for await (const value of generateSequence(1, 5)) {
    console.log(value); // 1, 2, 3, 4, 5 (outputs one every 100ms)
  }
})();

Error Handling in Async Generator Functions

Errors in async generator functions can be caught using try-catch blocks or handled via the catch method of the returned Promise:

async function* asyncGeneratorWithError() {
  try {
    yield await Promise.resolve('Success');
    throw new Error('Something went wrong');
  } catch (error) {
    yield `Caught error: ${error.message}`;
  }
}

const generator = asyncGeneratorWithError();
generator.next().then(console.log); // { value: 'Success', done: false }
generator.next().then(console.log); // { value: 'Caught error: Something went wrong', done: false }

Practical Use Cases for Async Generator Functions

  1. Paginated Data Loading: Implementing lazy-loading data streams
async function* paginatedFetcher(endpoint) {
  let page = 1;
  while (true) {
    const response = await fetch(`${endpoint}?page=${page}`);
    const data = await response.json();
    if (data.length === 0) return;
    yield data;
    page++;
  }
}
  1. WebSocket Message Processing: Handling real-time data streams
async function* webSocketListener(url) {
  const socket = new WebSocket(url);
  try {
    while (true) {
      const message = await new Promise(resolve => {
        socket.addEventListener('message', resolve, { once: true });
      });
      yield message.data;
    }
  } finally {
    socket.close();
  }
}

Differences Between Async Generator Functions and Regular Generator Functions

Feature Async Generator Functions Regular Generator Functions
Declaration async function* function*
Return Value Async generator object Generator object
next() Return Value Promise Plain object
Iterable Protocol Async iterator Sync iterator
Internal Usage await and yield Only yield

Combining Async Generator Functions

Async generator functions can be combined with other asynchronous operations to create complex data processing pipelines:

async function* mapAsync(asyncIterable, mapper) {
  for await (const item of asyncIterable) {
    yield await mapper(item);
  }
}

async function* filterAsync(asyncIterable, predicate) {
  for await (const item of asyncIterable) {
    if (await predicate(item)) {
      yield item;
    }
  }
}

(async () => {
  const numbers = generateSequence(1, 10);
  const squared = mapAsync(numbers, async n => n * n);
  const evenSquares = filterAsync(squared, async n => n % 2 === 0);
  
  for await (const num of evenSquares) {
    console.log(num); // 4, 16, 36, 64, 100
  }
})();

Performance Considerations for Async Generator Functions

When using async generator functions, keep in mind:

  1. Each yield creates a new Promise object
  2. Asynchronous operations introduce additional microtask queue overhead
  3. Memory usage may be higher than synchronous implementations
// Inefficient implementation
async function* inefficient() {
  for (let i = 0; i < 1e6; i++) {
    yield await Promise.resolve(i); // Creates a million Promises
  }
}

// Improved implementation
async function* efficient() {
  let buffer = [];
  for (let i = 0; i < 1e6; i++) {
    buffer.push(i);
    if (buffer.length >= 1000) {
      yield buffer;
      buffer = [];
    }
  }
  if (buffer.length > 0) yield buffer;
}

Advanced Patterns for Async Generator Functions

  1. Two-Way Communication: Passing values to the generator via next()
async function* twoWayCommunication() {
  const question1 = yield 'What is your name?';
  console.log(question1); // User input
  const question2 = yield `Hello ${question1}, how old are you?`;
  console.log(question2); // User input
}

async function run() {
  const generator = twoWayCommunication();
  let result = await generator.next();
  console.log(result.value); // "What is your name?"
  
  result = await generator.next('Alice');
  console.log(result.value); // "Hello Alice, how old are you?"
  
  await generator.next(30);
}
run();
  1. Timeout Control: Implementing an async generator with timeout
async function* withTimeout(asyncIterable, timeout) {
  for await (const item of asyncIterable) {
    yield await Promise.race([
      item,
      new Promise((_, reject) => 
        setTimeout(() => reject(new Error('Timeout')), timeout)
    ]);
  }
}

Async Generator Functions in Node.js

In Node.js, async generator functions are particularly well-suited for handling stream data:

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

async function* readLines(filePath) {
  const fileStream = fs.createReadStream(filePath);
  const rl = readline.createInterface({
    input: fileStream,
    crlfDelay: Infinity
  });

  try {
    for await (const line of rl) {
      yield line;
    }
  } finally {
    rl.close();
    fileStream.close();
  }
}

(async () => {
  for await (const line of readLines('large-file.txt')) {
    console.log(line);
  }
})();

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

上一篇:async函数返回值

下一篇:top-level await

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.