typescript / expert
Snippet
Async-Paginierung mit Symbol.asyncIterator
Wer Symbol.asyncIterator implementiert, macht aus einer cursor-basierten Quelle ein vollwertiges for-await-of-Iterable. Der Async-Generator hält den Cursor auf dem Call-Stack statt in Instanzfeldern, sodass parallele Iterationen voneinander unabhängig bleiben. Konsumenten sehen einen flachen Item-Strom, während die Klasse intern seitenweise nachlädt.
snippet.ts
typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class PageStream<T> implements AsyncIterable<T> {constructor(private fetchPage: (cursor?: string) => Promise<{ items: T[]; next?: string }>,) {}async *[Symbol.asyncIterator](): AsyncIterator<T> {let cursor: string | undefined;do {const { items, next } = await this.fetchPage(cursor);for (const item of items) yield item;cursor = next;} while (cursor);}}const ids = new PageStream<number>(async (c) =>c ? { items: [3, 4] } : { items: [1, 2], next: 'p2' },);for await (const id of ids) console.log(id);
Erklärung
1
class PageStream<T> implements AsyncIterable<T> {
Instanzen erfüllen das AsyncIterable-Protokoll für einen beliebigen Elementtyp T.
2
async *[Symbol.asyncIterator](): AsyncIterator<T> {
Eine Async-Generator-Methode mit berechnetem Namen ist der vorgeschriebene Einstiegspunkt des Protokolls.
3
const { items, next } = await this.fetchPage(cursor);
Pro Iteration wird genau eine Seite geladen und in Items plus optionalen Folge-Cursor zerlegt.
4
for (const item of items) yield item;
yield im Async-Generator reicht jedes Element einzeln an den for-await-of-Konsumenten weiter.
5
for await (const id of ids) console.log(id);
Der Konsument liest scheinbar synchron, obwohl Seiten erst bei Bedarf geladen werden.