Async Iterators / for-await-of
В одной фразе: async iterator — как обычный iterator, но next() возвращает Promise<{value, done}>, а for-await-of последовательно await-ит каждый элемент. Идеален для стриминга данных, пагинации и WebSocket-сообщений.
// Async generator — самый удобный способ создать async iterableasync function* paginate(url) { let page = 1; while (true) { const resp = await fetch(`${url}?page=${page}`); const data = await resp.json(); if (!data.items.length) return; // done: true yield* data.items; // отдаём каждый элемент if (!data.hasNext) return; page++; }}
async function processAll() { for await (const item of paginate('/api/items')) { await processItem(item); // последовательная обработка }}// Реализация async iterator вручную (push-queue паттерн)function createAsyncQueue() { const queue = []; let pending = null; // ожидающий resolve
return { push(value) { if (pending) { pending({ value, done: false }); pending = null; } else queue.push(value); }, end() { if (pending) pending({ value: undefined, done: true }); },
[Symbol.asyncIterator]() { return { next() { if (queue.length) return Promise.resolve({ value: queue.shift(), done: false }); return new Promise(resolve => { pending = resolve; }); } }; } };}// for-await-of: последовательно (не параллельно!)async function sequential(urls) { for await (const url of urls) { const data = await fetch(url).then(r => r.json()); console.log(data); // каждый URL ждёт предыдущего }}
// Для параллельной обработки: Promise.allasync function parallel(urls) { const results = await Promise.all(urls.map(u => fetch(u).then(r => r.json()))); results.forEach(data => console.log(data));}
// for-await-of только в async функции или ESM top-level await// В CommonJS нужна обёртка: (async () => { for await (...) })()Итог: Async iterators позволяют работать с потоками данных через знакомый синтаксис for-of. Node.js Readable streams реализуют async iterable protocol.