Async Iterators: асинхронный перебор
Что такое Async Iterators? 🤔
Представь, что ты пишешь код, который должен обрабатывать данные, поступающие постепенно — например, из файла, базы данных или API. Обычные итераторы здесь не помогут, ведь они работают синхронно. Асинхронные итераторы (Async Iterators) решают эту проблему, позволяя перебирать данные неблокирующим способом.
🔥 Ключевая идея: Async Iterators — это итераторы, которые умеют ждать (
await) перед возвращением следующего значения.
Как устроен асинхронный итератор? 🧩
Асинхронный итератор — это объект, который реализует:
- Метод
Symbol.asyncIterator(аналогSymbol.iteratorдля синхронных итераторов). - Метод
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? 🎯
- Потоковая обработка данных: файлы, сетевые запросы, базы данных.
- Работа с событиями: WebSockets, Server-Sent Events.
- Пользовательские источники данных: когда данные приходят частями.
📌 Совет от Данилы Бежина: Если ты ещё не подписан на его YouTube, то зря! Там есть разбор сложных тем вроде этой, но с шутками и жизнью.
Итоги ✨
- Async Iterators позволяют работать с асинхронными последовательностями данных.
for await...of— главный инструмент для перебора.- Асинхронные генераторы (
async function*) упрощают создание кастомных итераторов. - Ошибки обрабатываются стандартным
try/catch.
Теперь ты готов работать с данными, которые приходят не сразу, а по чуть-чуть — как пицца курьером, когда очень голоден. 🍕