Async Iterators: асинхронный перебор

Что такое Async Iterators? 🤔

Представь, что ты пишешь код, который должен обрабатывать данные, поступающие постепенно — например, из файла, базы данных или API. Обычные итераторы здесь не помогут, ведь они работают синхронно. Асинхронные итераторы (Async Iterators) решают эту проблему, позволяя перебирать данные неблокирующим способом.

🔥 Ключевая идея: Async Iterators — это итераторы, которые умеют ждать (await) перед возвращением следующего значения.


Как устроен асинхронный итератор? 🧩

Асинхронный итератор — это объект, который реализует:

  1. Метод Symbol.asyncIterator (аналог Symbol.iterator для синхронных итераторов).
  2. Метод next(), который возвращает Promise с очередным значением.
const asyncIterable = {
  [Symbol.asyncIterator]() {
    let i = 0;
    return {
      next() {
        if (i < 3) {
          return Promise.resolve({ value: i++, done: false });
        }
        return Promise.resolve({ done: true });
      }
    };
  }
};

Цикл for await...of 🔄

Для перебора асинхронных итераторов используется специальный синтаксис:

async function processAsyncData() {
  for await (const item of asyncIterable) {
    console.log(item); // 0, 1, 2
  }
}

processAsyncData();

💡 Важно: for await...of работает только внутри async-функций!


Реальный пример: Чтение файла построчно 📂

Допустим, у нас есть большой файл, и мы хотим читать его постепенно, без загрузки в память целиком. В Node.js для этого есть модуль fs с поддержкой асинхронных итераторов:

import { createReadStream } from 'fs';
import { createInterface } from 'readline';

async function readFileLines(path) {
  const fileStream = createReadStream(path);
  const rl = createInterface({
    input: fileStream,
    crlfDelay: Infinity
  });

  for await (const line of rl) {
    console.log(`Строка: ${line}`);
  }
}

readFileLines('./big-file.txt');

Создаём свой асинхронный генератор 🛠️

Асинхронные генераторы (async function*) — это удобный способ создавать асинхронные итераторы. Они могут yield-ить значения с await:

async function* fetchPages(urls) {
  for (const url of urls) {
    const response = await fetch(url);
    yield response.json(); // Возвращаем данные по мере получения
  }
}

// Использование
const urls = ['/api/page1', '/api/page2', '/api/page3'];

(async () => {
  for await (const pageData of fetchPages(urls)) {
    renderPage(pageData); // Обрабатываем каждую страницу
  }
})();

Обработка ошибок в асинхронных итераторах ⚠️

Асинхронные итераторы могут выбрасывать ошибки. Обрабатывать их можно через try/catch:

async function* riskyAsyncGenerator() {
  yield 1;
  throw new Error('Что-то пошло не так!');
  yield 2; // Не выполнится
}

(async () => {
  try {
    for await (const num of riskyAsyncGenerator()) {
      console.log(num);
    }
  } catch (err) {
    console.error('Ошибка:', err.message); // Ловим ошибку
  }
})();

Когда использовать Async Iterators? 🎯

  1. Потоковая обработка данных: файлы, сетевые запросы, базы данных.
  2. Работа с событиями: WebSockets, Server-Sent Events.
  3. Пользовательские источники данных: когда данные приходят частями.

📌 Совет от Данилы Бежина: Если ты ещё не подписан на его YouTube, то зря! Там есть разбор сложных тем вроде этой, но с шутками и жизнью.


Итоги ✨

  • Async Iterators позволяют работать с асинхронными последовательностями данных.
  • for await...of — главный инструмент для перебора.
  • Асинхронные генераторы (async function*) упрощают создание кастомных итераторов.
  • Ошибки обрабатываются стандартным try/catch.

Теперь ты готов работать с данными, которые приходят не сразу, а по чуть-чуть — как пицца курьером, когда очень голоден. 🍕

Скрыть рекламу навсегда

🧠 Учёба без воды и зубрёжки

Закрытый Boosty с наработками опытного преподавателя.

Объясняю сложное так, чтобы щелкнуло.

🚀 Забрать доступ к Boosty