I am fetching data from backend service, 20 results at a time. I would like to hide this implementation detail and create a generator that would keep returning records for as long as I need them (while they are available).
Naive implementation:
function* getEndlessRecords(fetchingFunction) {
const batchSize = 20;
// endless loop:
for (let offset = 0; true; offset += batchSize) {
fetchingFunction(offset, batchSize)
.then(records => {
for (let i=0; i < records.length; i++) {
yield records[i]; // THIS DOESN'T WORK!!!
}
})
}
}
(there might be typos - this is simplified code)
I understand why this doesn't work (yield
works on innermost function), however I can't seem to find a nice way to create a generator around the async functions.
Is it possible for generator to consume output from async functions?
As of ES2018, you could use an async generator function:
async function* getEndlessRecords(fetchingFunction) {
const batchSize = 20;
// endless loop:
for (let offset = 0; true; offset += batchSize) {
const records = await fetchingFunction(offset, batchSize);
for (let i=0; i < records.length; i++) {
yield records[i];
}
}
}
You'd consume that in an async
function using for-await-of
(not for-of
):
for await (const value of getEndlessRecords(/*...*/)) {
// Do something with `value`
}
...or just by calling its next
method and awaiting the result:
let g = getEndlessRecords(/*...*/);
let result;
while (!(result = await g.next()).done) {
console.log(result.value);
}
...or of course, in a non-async
function, you'd use then
on the result of g.next()
.
Prior to ES2018's async generator function syntax, you'd have to hand-code the generator rather than using function*
syntax. Doing so arguably-incorrectly (not allowing for any extensions to %GeneratorPrototype%) is fairly easy. Doing so correctly is fairly awkward, since %GeneratorPrototype% doesn't have a publicly-accessible symbol and you have to go discover it.