Async Generator
Async generators (`async function*`) and `for await...of`.
An async generator is a coroutine that both yields values to its consumer and awaits between yields. Its .next returns a Promise<{value, done}>, which a `for await...of` loop awaits each step. No network here — it awaits an ordinary async function — so this runs offline and deterministically.
async function slowSquare(n: number): Promise<number> {
return n * n
}
// Yields the running squares 1, 4, 9, 16, awaiting each computation.
async function* squares(limit: number): number {
let i: number = 1
while (i <= limit) {
yield await slowSquare(i)
i = i + 1
}
}
// A throwing async generator rejects the outstanding .next() promise.
async function* boom(): number {
yield 1
throw new Error("stop")
}
// An async generator can yield objects (or tuples), and `for await...of` can
// destructure each yielded element straight into its own bindings.
async function* points(): { x: number; y: number } {
yield { x: 1, y: 2 }
yield { x: 3, y: 4 }
}
async function main(): Promise<void> {
for await (const sq of squares(4)) {
console.log(sq) // 1, 4, 9, 16
}
// Destructuring loop variable: bind x and y from each yielded point.
for await (const { x, y } of points()) {
console.log(x + y) // 3, 7
}
// Manual iteration: .next() returns a Promise<{value, done}>.
const it = squares(1)
const first = await it.next()
console.log(first.value + " done=" + first.done) // 1 done=false
const end = await it.next()
console.log("done=" + end.done) // done=true
// .next() returns a genuinely-pending promise: the generator body runs as a
// microtask, not synchronously at the .next() call. So a side effect before the
// first yield is deferred past the code right after .next() — matching JS's
// "before, after, body" ordering (ADR-00275).
const d = deferredGen()
console.log("before") // before
const dp = d.next()
console.log("after") // after
console.log((await dp).value) // body (from the gen), then 1
const b = boom()
console.log((await b.next()).value) // 1
try {
await b.next()
} catch (e) {
console.log("caught " + e.message) // caught stop
}
// .throw(e) injects an error at the suspension point; a body try/catch handles
// it (and may await), so the .throw() promise fulfils with the recovered value.
const g = guardedGen()
console.log((await g.next()).value) // 1
console.log((await g.throw(new Error("oops"))).value) // 42 (resumed in catch)
// .return(v) completes an async generator early, running its finally block.
const c = cleanupGen()
console.log((await c.next()).value) // 1
const r = await c.return(7)
console.log(r.value + " done=" + r.done) // 7 done=true
// yield* delegates to an inner async generator: each inner step is awaited,
// its values re-yielded, and its return value becomes the yield* result.
for await (const v of composed()) {
console.log(v) // 1, 4, then 0
}
}
async function* firstTwoSquares(): number {
yield await slowSquare(1) // 1
yield await slowSquare(2) // 4
return 2 // count produced
}
async function* composed(): number {
const count = yield* firstTwoSquares()
console.log("delegated " + count + " values") // delegated 2 values
yield 0
}
async function* guardedGen(): number {
try {
yield await slowSquare(1)
yield await slowSquare(2)
} catch (e) {
console.log("gen caught " + e.message) // gen caught oops
yield 42
}
}
async function* cleanupGen(): number {
try {
yield 1
yield 2
} finally {
console.log("gen cleanup") // runs on .return()
}
}
// A side effect before the first yield: with a deferred .next(), it prints only
// once the microtask step runs — after "before"/"after" (see main()).
async function* deferredGen(): number {
console.log("body")
yield 1
}
main()View source on GitHubexamples/async/async_generator.ts