Geradores Assíncronos em JavaScript
Os iteradores assíncronos nos permitem criar geradores assíncronos. Eles possibilitam o uso do operador await
para receber e retornar dados de forma assíncrona.
Para criar um gerador assíncrono, basta adicionar o operador async
antes da função geradora:
async function* nomeDaFuncaoGeradora() {
yield valorRetornado;
}
Vamos considerar um gerador simples:
async function* generatePersonAsync() {
yield "Tom";
}
Aqui, definimos o gerador assíncrono generatePersonAsync
, que retorna um valor, a string "Tom".
Uma característica dos geradores assíncronos é que, ao chamar seu método next()
, ele retorna um objeto Promise
. Esse Promise
retorna um objeto com duas propriedades: { value, done }
, onde value contém o valor retornado e done
indica se ainda há dados disponíveis no gerador.
Por exemplo, considerando o gerador assíncrono definido acima:
async function* generatePersonAsync() {
yield "Tom";
}
const personGenerator = generatePersonAsync();
personGenerator.next(); // Promise
Aqui, ao chamar o método next()
, obtemos uma promise. Através do método then()
, podemos extrair o objeto da promise:
const personGenerator = generatePersonAsync();
personGenerator.next().then((data) => console.log(data)); // {value: "Tom", done: false}
E ao acessar a propriedade value do objeto retornado pela promise, podemos obter os dados:
const personGenerator = generatePersonAsync();
personGenerator.next().then((data) => console.log(data.value)); // Tom
Podemos usar o operador await
para obter os dados do método next()
do gerador:
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();
A saída no console será:
Tom Sam Bob
Como o gerador assíncrono é um iterador assíncrono, também podemos obter seus dados usando o loop 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
Uso do await em Geradores Assíncronos
A principal vantagem dos geradores assíncronos é a possibilidade de usar o operador await
para obter dados de fontes que utilizam APIs assíncronas.
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"]);
Aqui, para simular a obtenção de dados de uma fonte assíncrona, usamos uma promise que, após 2000 milissegundos, retorna um dos elementos do array passado para a função geradora:
yield await new Promise((resolve) => setTimeout(() => resolve(person), 2000));