Async Iterators & for-await-of
An async iterator is an object whose next() returns a promise of {' '} , 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.
📚 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.
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 {' '} . 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:
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.
Replace the blank with the correct loop keyword to consume the async generator.
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:
Make the generator emit the squares 1, 4, 9 by replacing the blank with yield .
Predict the output before revealing the answer.
1 false — next() returns a promise of {' '} , 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 .
Build an async generator onlyEven that wraps another async iterable and yields only its even numbers. This is how you compose streaming pipelines.
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.