Reputation: 6041
I'm reading a book on Modern Asynchronous Javascript, and at one point the author writes some code to query a Github repo and retrieve all of its past commits.
The resulting array gives us 30 commits at a time, and the response header provides us with a link to the next page that contains additional results.
In the book, the author uses a generator function to accomplish this task like this:
async function* generator(repo) {
for (;;) {
const response = await axios.get(repo, config);
const data = response.data;
for (let commit of data) {
yield commit;
}
const link = response.headers.link;
repo = /<(.*?)>; rel="next"/.exec(link)?.[1];
if (repo === undefined) {
// Breaking doesn't return anything, which means the iterator is done
break;
}
}
}
async function getCommits(repo, maxData) {
let i = 0;
for await (const commit of generator(repo)) {
console.log(`-------------repo: ${i}-----------------`);
console.log(commit);
if (i++ === maxData) {
break;
}
}
}
In trying to understand generator functions, I ended up rewriting this code how I would normally do it with a regular asynchronous function like this:
async function getCommits(url, numItems) {
let i = 0;
while (i <= numItems) {
let commits = await axios.get(url, config);
let data = commits.data;
let nextUrl = /<(.*?)>; rel="next"/.exec(commits.headers.link)?.[1];
for (let commit of data) {
if (i > numItems) break;
console.log(`-------------repo: ${i}-----------------`);
console.log(commit);
i++;
}
url = nextUrl;
}
}
My code seems to work fine, and it seems to be shorter than the version using the generator function. I still don't fully understand the use-case for asynchronous generators or why I would ever use them.
Would love if someone could explain or point me towards some resources for learning more.
Upvotes: 3
Views: 574
Reputation: 370679
One of the purposes is to abstract the asynchronous actions. Say there's a library that has a function that does a number of asynchronous tasks. While it could instruct users of the library to perform the iterations themselves - such as with your second code - it's arguably nicer to provide users with an async generator instead.
If you implement the generator once, that generator can then be used wherever it's needed, instead of having to write imperative iterative code.
It's nicer to be able to say
// Here's how you use this library
for await (const result of getResults()) {
console.log(result);
}
than
// Here's how you use this library
let i = 0;
const resultsGetter = makeAllResultsGetter();
while (i <= numItems) {
const result = await resultsGetter();
console.log(result);
i++;
}
You definitely don't need to use async generators, but you may one time come across a situation where you find that the logic pattern they provide is the most elegant approach to the problem at hand.
Upvotes: 2