This code works as expected:
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function getAsyncData() {
await sleep(1000); // simulate database/network delay...
return [1, 2, 3, 4, 5]; // ...then return some data
}
const asyncIterable = (async function* filterAsyncData() {
const items = await getAsyncData();
for (const item of items) {
yield item;
}
})();
const asyncIterable2 = {
[Symbol.asyncIterator]() {
return {
values: null,
idx: 0,
async next() {
if (this.values === null) {
this.values = await getAsyncData();
}
if (this.idx < this.values.length) {
this.idx = this.idx + 1;
return Promise.resolve({ value: this.values[this.idx - 1], done: false });
}
return Promise.resolve({ done: true });
}
};
}
};
async function main() {
for await (const filteredItem of asyncIterable) {
console.log(filteredItem);
}
}
main()
It does not mather if I use asyncIterable
or asyncIterable2
in the main
function, I always get the same result. What is the best practice to define my iterable? Are there any guidelines about which option is preferred? Why?
This code works as expected:
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function getAsyncData() {
await sleep(1000); // simulate database/network delay...
return [1, 2, 3, 4, 5]; // ...then return some data
}
const asyncIterable = (async function* filterAsyncData() {
const items = await getAsyncData();
for (const item of items) {
yield item;
}
})();
const asyncIterable2 = {
[Symbol.asyncIterator]() {
return {
values: null,
idx: 0,
async next() {
if (this.values === null) {
this.values = await getAsyncData();
}
if (this.idx < this.values.length) {
this.idx = this.idx + 1;
return Promise.resolve({ value: this.values[this.idx - 1], done: false });
}
return Promise.resolve({ done: true });
}
};
}
};
async function main() {
for await (const filteredItem of asyncIterable) {
console.log(filteredItem);
}
}
main()
It does not mather if I use asyncIterable
or asyncIterable2
in the main
function, I always get the same result. What is the best practice to define my iterable? Are there any guidelines about which option is preferred? Why?
-
1
In this particular case, you actually should be doing
for (const filteredItem of await getAsyncData()) {
instead of using async iterators/iterables at all. – Bergi Commented Jul 14, 2020 at 16:35 - You are pletely right! But I do need the iterator because I am mocking the return value of an async generator function in a test :) – enanone Commented Jul 14, 2020 at 16:37
1 Answer
Reset to default 8It's the same as for synchronous iterators: generator functions are much easier to write, and easier to get correct, than implementing the iterator object manually. Do this only if you need some non-standard behaviour that cannot be achieved otherwise. With asynchronous generator functions specifically, you even get the proper queueing of next
calls for free, which is a real headache to get right (your asyncIterable2
fails this1).
The most mon implementation of iterables is to make the Symbol.asyncIterator
method an async generator method:
const asyncIterable = {
async *[Symbol.asyncIterator]() {
yield* await getAsyncData();
},
};
1: const it = asyncIterable2[Symbol.asyncIterator](); it.next(); it.next()
- without any await
s in between - will call getAsyncData
twice, because this.values == null
in both calls