Асинхронные итераторы открывают нам путь к созданию асинхронных генераторов. Асинхронные генераторы позволяют нам использовать оператор await и получать и возвращать данные асинхронным образом.
Для определения асинхронного генератора перед функцией генератора ставится оператор async
async function* название_функции_генератора(){ yield возвращаемое_значение; }
Рассмотрим простейший генератор:
async function* generatePersonAsync(){ yield "Tom"; }
Здесь определен асинхронный генератор generatePersonAsync, в котором возвращается одно значение - строка "Tom".
Особенностью асинхронного генератора является то, что при обращении к его методу next() возвращается объект Promise.
А полученный объект Promise, в свою очередь, возвращает объект с двумя свойствами { value, done }
, где value
собственно хранит возвращаемое
значение, а done
указывает, доступны ли в генераторе еще данные.
Например, возьмем выше определенный асинхронный генератор:
async function* generatePersonAsync(){ yield "Tom"; } const personGenerator = generatePersonAsync(); personGenerator.next(); // Promise
Здесь с помощью метода next()
получаем промис. Далее через метод then()
мы можем получить из промиса объект:
const personGenerator = generatePersonAsync(); personGenerator.next() .then(data => console.log(data)); // {value: "Tom", done: false}
И при обращении к свойству value
полученного из промиса получить сами данные:
const personGenerator = generatePersonAsync(); personGenerator.next() .then(data => console.log(data.value)); // Tom
С помощью оператора await из метода next()
генератора мы можем получить данные:
async function* generatePersonAsync(){ yield "Tom"; yield "Sam"; yield "Bob"; } async function printPeopleAsync(){ const personGenerator = generatePersonAsync(); while(!(person = await personGenerator.next()).done){ console.log(person.value); } } printPeopleAsync();
Консольный вывод:
Tom Sam Bob
Поскольку асинхронный генератор представляет асинхронный итератор, то данные генератора также можно получить через цикл for-await-of:
async function* generatePersonAsync(){ yield "Tom"; yield "Sam"; yield "Bob"; } async function printPeopleAsync(){ const personGenerator = generatePersonAsync(); for await(person of personGenerator){ console.log(person); } } printPeopleAsync(); // Tom // Sam // Bob
Главным преимуществом асинхронным генераторов является то, что мы можем использовать в них оператор await и соответственно получать данные из источников данных, которые используют асинхронный API.
async function* generatePersonAsync(people){ for(const person of people) yield await new Promise(resolve => setTimeout(() => resolve(person), 2000)); } async function printPeopleAsync(people){ for await (const item of generatePersonAsync(people)) { console.log(item); } } printPeopleAsync(["Tom", "Sam", "Bob"]);
Здесь для имитации получения данных из асинхронного источника данных применяется промис, который через 2000 секуд возвращает один из элементов массива, который передается в функцию генератора:
yield await new Promise(resolve => setTimeout(() => resolve(person), 2000));