Асинхронные итераторы открывают нам путь к созданию асинхронных генераторов. Асинхронные генераторы позволяют нам использовать оператор 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));