Iteradores Assíncronos - JavaScript
Iteradores assíncronos combinam as capacidades de iteradores com os operadores async
await
Do tema sobre iteradores, sabemos que um iterador fornece o método next()
{ value, done }
value
for..of
done
done
false
done
true
Um iterador assíncrono é semelhante a um iterador síncrono, exceto que seu método next()
Promise
Promise
{ value, done }
Loop for-await-of
Para obter dados usando iteradores assíncronos, usamos o loop for-await-of
for await (variable of iterable) {
// ações
}
No loop for-await-of
of
String
Map
Set
Vale notar que esta forma de loop só pode ser usada em funções definidas com o operador async
Vejamos um exemplo simples onde a fonte de dados é um array comum:
const dataSource = ["Tom", "Sam", "Bob"];
async function readData() {
for await (const item of dataSource) {
console.log(item);
}
}
readData();
// Tom
// Sam
// Bob
Aqui, o array dataSource
[Symbol.asyncIterator]()
Promise
Criando um Iterador Assíncrono
No exemplo acima, o iterador assíncrono foi criado implicitamente. No entanto, podemos defini-lo explicitamente. Por exemplo, vamos definir um iterador assíncrono que retorna elementos de um array:
const generatePerson = {
[Symbol.asyncIterator]() {
return {
index: 0,
people: ["Tom", "Sam", "Bob"],
next() {
if (this.index < this.people.length) {
return Promise.resolve({ value: this.people[this.index++], done: false });
}
return Promise.resolve({ done: true });
}
};
}
};
Aqui, definimos o objeto generatePerson
[Symbol.asyncIterator]()
generatePerson
Pontos principais de um iterador assíncrono:
Um iterador assíncrono é implementado com o método
que retorna um objeto.[Symbol.asyncIterator]()
O objeto retornado pelo iterador possui o método
que retorna um objetonext()
.Promise
O objeto
retorna um objeto com duas propriedadesPromise
. A propriedade{ value, done }
contém um valor, enquantovalue
indica se a iteração foi concluída. Sedone
fordone
, não há mais objetos para iterar, etrue
não precisa ser especificado.value
No exemplo, o iterador retorna usuários sequencialmente. Para armazenar os usuários, usamos um array people
index
index: 0,
people: ["Tom", "Sam", "Bob"],
No método next()
Promise
Promise
return Promise.resolve({ value: this.people[this.index++], done: false });
Se todos os elementos do array foram obtidos, retornamos um Promise
{ done: true }
Isso indica ao código externo que todos os valores do iterador já foram obtidos.
Agora, vejamos como obter dados do iterador:
Podemos acessar o próprio iterador assíncrono:
generatePerson[Symbol.asyncIterator](); // obtenha o iterador assíncrono
E chamar explicitamente seu método next()
generatePerson[Symbol.asyncIterator]().next(); // Promise
Este método retorna uma Promise
then()
generatePerson[Symbol.asyncIterator]()
.next()
.then((data) => console.log(data)); // {value: "Tom", done: false}
O objeto retornado pela Promise
value
done
generatePerson[Symbol.asyncIterator]()
.next()
.then((data) => console.log(data.value)); // Tom
Como o método next()
Promise
await
async function printPeople() {
const peopleIterator = generatePerson[Symbol.asyncIterator]();
let personData;
while(!(personData = await peopleIterator.next()).done) {
console.log(personData.value);
}
}
printPeople();
Aqui, em uma função assíncrona com um loop while
await
Promise
No entanto, para iterar sobre um objeto de iterador assíncrono, é mais simples usar o loop for-await-of
const generatePerson = {
people: ["Tom", "Sam", "Bob"],
[Symbol.asyncIterator]() {
let index = 0;
return {
next: async () => {
if (index < this.people.length) {
// Simulando um atraso assíncrono de 2 segundos para cada pessoa
const value = await new Promise(resolve =>
setTimeout(() => resolve(this.people[index++]), 2000)
);
return { value, done: false };
}
return { done: true };
}
};
}
};
async function printPeople() {
for await (const person of generatePerson) {
console.log(person);
}
}
printPeople();
Como o objeto generatePerson
[Symbol.asyncIterator]()
for-await-of
next()
Promise
Tom Sam Bob
Vale ressaltar que não podemos usar um loop for-of
Outro exemplo simples é a obtenção de números:
const generateNumber = {
[Symbol.asyncIterator]() {
return {
current: 0,
end: 10,
next() {
if (this.current <= this.end) {
return Promise.resolve({ value: this.current++, done: false });
}
return Promise.resolve({ done: true });
}
};
}
};
async function printNumbers(){
for await (const n of generateNumber) {
console.log(n);
}
}
printNumbers();
Neste exemplo, o iterador assíncrono do objeto generateNumber
Conclusão
Iteradores assíncronos são uma maneira eficaz de acessar fontes de dados assíncronas. Eles combinam as capacidades de iteradores com os operadores async
await