JavaScript AsyncIterator
An AsyncIterator is just like a normal iterator, except every .next() call returns a Promise.
1. The 30-second mental model
| Normal Iterator | AsyncIterator |
|---|---|
| Gives the next value synchronously | Gives the next value asynchronously |
Works with for…of | Works with for await…of |
.next() returns {value, done} | .next() returns Promise<{value, done}> |
2. The easiest possible AsyncIterator
An async generator function (async function*) automatically returns an AsyncIterator.
// 1️⃣ create
async function* countSlowly() {
for (let i = 1; i <= 3; i++) {
await new Promise(r => setTimeout(r, 500)); // wait half a second
yield i; // hand out the next number
}
}
// 2️⃣ use
(async () => {
for await (const n of countSlowly()) {
console.log(n);
}
console.log('Done!');
})();
Run this in any browser console or Node.js and you’ll see:
1 (after ~0.5 s)
2 (after ~1.0 s)
3 (after ~1.5 s)
Done!
That’s it—no extra classes or libraries.
3. One more practical example: “Fetch tweets”
Imagine we’re receiving a page of tweets at a time from an API.
// fake async fetcher
async function fetchPage(page) {
await new Promise(r => setTimeout(r, 300)); // network delay
const tweets = [`tweet #${page * 2 - 1}`, `tweet #${page * 2}`];
return { tweets, hasMore: page < 3 };
}
// our AsyncIterator
async function* tweetStream() {
let page = 1;
while (true) {
const { tweets, hasMore } = await fetchPage(page++);
for (const t of tweets) yield t;
if (!hasMore) break;
}
}
// consume it
(async () => {
for await (const tw of tweetStream()) {
console.log('📢', tw);
}
})();
Output (with tiny pauses):
📢 tweet #1
📢 tweet #2
📢 tweet #3
📢 tweet #4
📢 tweet #5
📢 tweet #6
4. DIY object that fits for await…of
If you want to build your own AsyncIterator without generators, give it a next() returning a Promise and tag it with Symbol.asyncIterator:
const threeColours = {
colours: ['red', 'green', 'blue'],
index: 0,
async next() {
if (this.index === this.colours.length) {
return { value: undefined, done: true };
}
await new Promise(r => setTimeout(r, 400)); // pretend delay
return { value: this.colours[this.index++], done: false };
},
[Symbol.asyncIterator]() {
return this; // make it usable with for await…of
}
};
(async () => {
for await (const colour of threeColours) {
console.log(colour);
}
})();
5. Quick cheat-sheet
- Create:
async function* myGen() { … } - Consume:
for await (const x of myGen()) { … } - Manual: object with
next() → Promiseand[Symbol.asyncIterator]() - Always works in async functions / modules; top-level
awaitis OK!