JS Advanced

Асинхронные генераторы

Понять, как async function* создаёт асинхронный генератор, и как for await...of перебирает асинхронные итераторы.

Продвинутый (Advanced)

Кратко

Async generator — async function* — функция, которая может приостанавливаться (yield) и выполнять асинхронные операции. for await...of перебирает асинхронные итераторы. Используется для: построчного чтения файлов, обработки стримов, постраничной загрузки данных.


Подробно

Async generator: async function* fetchPages(url) { let page = 1; while (true) { const response = await fetch(${url}?page=${page}); const data = await response.json(); if (data.length === 0) break; yield data; page++; } }

// Перебор: for await (const pageData of fetchPages('/api/users')) { console.log(pageData); }

Где используется:

  • Построчное чтение файлов (Node.js)
  • Постраничная загрузка
  • WebSocket-like стримы
  • Обработка данных по мере поступления

Синтаксис: async function* name() { yield await promise; }

// Или стрелочная (ограниченно) const gen = async function*() { yield 1; };


Ментальная модель

Async generator — как автомат с напитками с кнопкой «подождать». Вы нажимаете кнопку (next()) — автомат готовит напиток (await) — выдаёт его (yield). while(true) — автомат работает бесконечно, пока не закончатся напитки.


Примеры

**Самый простой:**

javascript
async function* numbers() {
  yield 1;
  yield new Promise(resolve => setTimeout(() => resolve(2), 100));
  yield 3;
}

for await (const n of numbers()) {
  console.log(n); // 1, 2 (через 100мс), 3
}

Простейший async generator: yield с Promise.

**Простой:**

javascript
async function* fetchAll(urls) {
  for (const url of urls) {
    const response = await fetch(url);
    const data = await response.json();
    yield { url, data };
  }
}

const urls = ['/api/users', '/api/posts'];
for await (const { url, data } of fetchAll(urls)) {
  console.log(`${url}: ${data.length} items`);
}

Последовательная загрузка данных с yield.

**Реальный пример:**

javascript
// Постраничная загрузка
async function* paginate(url, pageSize = 10) {
  let offset = 0;
  while (true) {
    const response = await fetch(
      `${url}?offset=${offset}&limit=${pageSize}`
    );
    const items = await response.json();
    if (items.length === 0) return;
    yield* items;
    offset += pageSize;
  }
}

// Загружаем все страницы
for await (const user of paginate('/api/users')) {
  console.log(user.name);
}

Постраничная загрузка: yield* для разворачивания массива.


Частые ошибки

**Неправильно:** Забывать await перед fetch в async generator

**Почему:** yield не ждёт Promise автоматически — нужен await.

**Правильно:** yield await promise; — правильный синтаксис.

**Неправильно:** Путать for...of и for await...of

**Почему:** for...of — для синхронных итераторов. for await...of — для асинхронных.

**Правильно:** async generator → for await...of. Обычный generator → for...of.


Важно запомнить

  • async function* создаёт асинхронный генератор
  • yield возвращает Promise
  • for await...of перебирает асинхронные итераторы
  • Используйте для: стримов, пагинации, построчного чтения
  • return в async generator завершает итерацию

Связь

Назад: Вы знаете async/await (JA5) и генераторы (JV8). Асинхронные генераторы объединяют оба понятия.

Вперёд: Следующий урок (JV10) — промисификация — преобразование колбэков в Promise.

Материалы курса JS Advanced — PROlab Academy.