Async Iterators & for-await-of

An async iterator is an object whose next() returns a promise of { value, done } , letting you pull values that arrive over time — one awaited step at a time — using the for-await-of loop.

Learn Async Iterators & for-await-of in our free JavaScript course — an interactive lesson with runnable examples, a practice exercise and a quick reference.

Part of the free JavaScript course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

They power streaming: reading a file line by line, paginating an API, or consuming a live feed without ever loading everything into memory at once.

What You'll Learn in This Lesson

📚 Prerequisites: Make sure you know async/await and promises . Async iterators glue those together with looping.

📬 Real-World Analogy: A regular array is like a stack of mail already on your desk — all there, read it whenever. An async iterator is like checking your mailbox: each trip might involve a wait for the next letter to arrive, and you only hold one letter at a time. for-await-of is you walking to the mailbox over and over until there is nothing left.

1️⃣ The Protocol — Symbol.asyncIterator

An object is async iterable if it has a method keyed by Symbol.asyncIterator that returns an async iterator — an object with a next() method that returns a promise of { value, done } . The for-await-of loop awaits each of those promises for you.

Here it is written out by hand so you can see exactly what the loop is driving:

2️⃣ Async Generators — the Easy Way

Writing the protocol by hand is tedious. async function* builds the whole thing for you: each yield produces a value, and because the function is async you can await between yields. Calling it returns a ready-to-loop async iterable.

This is the real streaming pattern — here a generator emits a tick every short delay, exactly how you might stream rows from a database cursor:

💡 Tip: The values are produced lazily . The generator is paused at each yield until the loop asks for the next one, so nothing runs ahead of what you consume.

🎯 Your Turn #1 — Loop an Async Generator

Replace the blank with the correct loop keyword to consume the async generator.

3️⃣ Real Use — Paginating an API Lazily

The killer app for async generators is pagination . Wrap the "fetch a page, yield each item, advance the cursor" loop in an async generator and the caller just writes a single for-await-of — completely unaware of pages, cursors, or network waits.

Below, a Map-backed source stands in for the network so you can watch the same pattern stream every item across "pages" — with no DOM or fetch required:

🎯 Your Turn #2 — Yield Values

Make the generator emit the squares 1, 4, 9 by replacing the blank with yield .

🧠 Quick Recall

Predict the output before revealing the answer.

1 false — next() returns a promise of { value, done } , and the first call yields 1 with done: false .

A TypeError — a plain for-of cannot drive an async iterable. You must use for await .

Symbol.asyncIterator — the sync version is Symbol.iterator .

Common Errors & Fixes

🎯 Mini Challenge — Async Filter

Build an async generator onlyEven that wraps another async iterable and yields only its even numbers. This is how you compose streaming pipelines.

Quick Reference — Async Iteration

Concept

Syntax

Async iterable hook

[Symbol.asyncIterator]() {}

Async generator

async function* g() {}

Produce a value

yield value;

Consume a stream

for await (const x of s) {}

Manual step

await it.next()

📚 Keep Learning

Frequently Asked Questions

🎉 Lesson Complete — Async Iterators!

Up next: AbortController & Cancelling Async Work — stop work you no longer need. ⏹️

Practice quiz

What does an async iterator's next() return?

  • A value directly
  • An array
  • A promise of { value, done }
  • void

Answer: A promise of { value, done }. Unlike a sync iterator, an async iterator's next() returns a PROMISE of { value, done }.

Which loop consumes an async iterable?

  • for-await-of
  • for-of
  • forEach
  • while

Answer: for-await-of. for-await-of awaits each promise from next(); a plain for-of cannot drive an async iterable.

What happens if you use a plain for-of on an async iterable?

  • It works fine
  • It silently does nothing
  • It returns promises
  • A TypeError — it is not iterable that way

Answer: A TypeError — it is not iterable that way. A plain for-of cannot drive an async iterable and throws a TypeError.

Which symbol marks an object as async iterable?

  • Symbol.iterator
  • Symbol.asyncIterator
  • Symbol.for('async')
  • Symbol.next

Answer: Symbol.asyncIterator. The method keyed by Symbol.asyncIterator is what for-await-of looks for; the sync version is Symbol.iterator.

What does 'async function*' create?

  • An async generator (an async iterable)
  • An async function
  • A normal generator
  • A promise

Answer: An async generator (an async iterable). async function* builds an async generator, the easiest way to produce an async iterable.

Inside an async generator, which keyword produces each value?

  • return
  • await
  • yield
  • emit

Answer: yield. Each yield produces a value, and you can await between yields because the function is async.

Why stream with for-await-of instead of collecting everything into an array first?

  • It is the only way that compiles
  • Memory stays flat — each value can be released as the next arrives
  • Arrays cannot hold promises
  • It is slower but simpler

Answer: Memory stays flat — each value can be released as the next arrives. Processing each value as it arrives keeps memory flat even for huge or endless streams.

For await const r = await g().next() on 'async function* g(){ yield 1; yield 2; }', what is r.value and r.done?

  • 1 and true
  • undefined and true
  • 2 and false
  • 1 and false

Answer: 1 and false. The first next() yields value 1 with done: false.

for-await-of must appear inside what?

  • A class method
  • An async function
  • A generator
  • Any function works

Answer: An async function. Using for await outside an async function is a SyntaxError.

What is the killer use case for async generators highlighted in the lesson?

  • Sorting arrays
  • Math calculations
  • Lazily paginating an API one page at a time
  • DOM manipulation

Answer: Lazily paginating an API one page at a time. Wrapping fetch-a-page-and-yield-each-item in an async generator lets the caller write a single for-await-of, unaware of paging.

Continue this course

Related lessons