Перейти к содержимому

Async Iterators / for-await-of

В одной фразе: async iterator — как обычный iterator, но next() возвращает Promise<{value, done}>, а for-await-of последовательно await-ит каждый элемент. Идеален для стриминга данных, пагинации и WebSocket-сообщений.

// Async generator — самый удобный способ создать async iterable
async 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 =&gt; { pending = resolve; });
}
};
}
};
}
// for-await-of: последовательно (не параллельно!)
async function sequential(urls) {
for await (const url of urls) {
const data = await fetch(url).then(r =&gt; r.json());
console.log(data); // каждый URL ждёт предыдущего
}
}
// Для параллельной обработки: Promise.all
async function parallel(urls) {
const results = await Promise.all(urls.map(u =&gt; fetch(u).then(r =&gt; r.json())));
results.forEach(data =&gt; console.log(data));
}
// for-await-of только в async функции или ESM top-level await
// В CommonJS нужна обёртка: (async () =&gt; { for await (...) })()

Итог: Async iterators позволяют работать с потоками данных через знакомый синтаксис for-of. Node.js Readable streams реализуют async iterable protocol.