Streams of Asynchronous Data

Sometimes data doesn't arrive all at once — it comes in chunks (like lines from a huge file, or pages from a paginated API). Async generators solve this elegantly.

async function* fetchAllCommits(repo) {
  let url = `https://api.github.com/repos/${repo}/commits`;
  
  while (url) {
    const response = await fetch(url); // Wait for each page
    const commits = await response.json();
    
    yield* commits; // Yield each commit one by one
    
    // Get the URL for the next page from the Link header
    const linkHeader = response.headers.get('Link');
    url = parsNextPageUrl(linkHeader); // null if no more pages
  }
}

// Consume: iterate through all commits, page by page
for await (const commit of fetchAllCommits('microsoft/vscode')) {
  console.log(commit.sha, commit.commit.message);
}

Why This Pattern Is Powerful

The consumer code is clean and sequential. It doesn't need to know about pagination at all — it just iterates until there's nothing left. The generator handles all the network logic internally.

Note

The for await...of loop is the special syntax designed specifically for consuming async iterables. It awaits each new value from the sequence.

Tip

This pattern is the foundation for working with browser Streams API and Node.js readable streams, which are used for processing large files, video, and network data.