With the growing popularity of the async / await construction, the interest in its internal mechanisms is growing. Having rummaged in the Internet, it is easy to find out that async / await is based on well-known promises, and generators, which are much less well-known and popular.

The material, the translation of which we are publishing today, is devoted to generators. Namely, here we will talk about how they work, and how they, together with promises, are used in the depths of the async / await construction. The author of this article says that generators, for the sake of their practical application, are not necessary to master. In addition, he notes that he expects that the reader is a little versed in promises.
Iterators and Generators
In JavaScript, starting with the release of the ES6 standard, several new features have appeared that are aimed at simplifying work with asynchronous data streams and collections.
Iterators and generators fall into this category.
')
A remarkable feature of the iterators is that they provide the means to access the elements of collections one at a time, while still being able to track the identifier of the current element.
function makeIterator(array) { var nextIndex = 0; console.log("nextIndex =>", nextIndex); return { next: function() { return nextIndex < array.length ? { value: array[nextIndex++], done: false } : { done: true }; } }; } var it = makeIterator(["simple", "iterator"]); console.log(it.next()); // {value: 'simple, done: false} console.log(it.next()); // {value: 'iterator, done: false} console.log(it.next()); // {done: true}
Above, we pass to the
makeIterator()
function a small array containing a couple of elements, and then we go through it using an iterator, calling the
it.next()
method. Pay attention to the comments demonstrating the results obtained using the iterator.
Now let's talk about the generators. Generators are functions that work as iterator factories. Consider a simple example, and then talk about two mechanisms related to generators.
function* sample() { yield "simple"; yield "generator"; } var it = sample(); console.log(it.next()); // {value: 'simple, done: false} console.log(it.next()); // {value: 'generator, done: false} console.log(it.next()); // {value: undefined, done: true}
Pay attention to the asterisk in the function declaration. This indicates that this function is a generator. Also, take a look at the
yield
keyword. It pauses the execution of the function and returns some value. Actually, these two features are the very two mechanisms that we talked about above:
- A generator function is a function declared using an asterisk near the
function
keyword or around the function name. - A generator iterator is created when a generator function is called.
In general, the above example demonstrates the work of a factory function that generates iterators.
Now that we have the basics, let's talk about more interesting things. Iterators and generators can exchange data in two directions. Namely, generators, using the
yield
keyword, can return values to iterators, however, iterators can also send data to generators using the
iterator.next('someValue')
method. Here's what it looks like.
function* favBeer() { const reply = yield "What is your favorite type of beer?"; console.log(reply); if (reply !== "ipa") return "No soup for you!"; return "OK, soup."; } { const it = favBeer(); const q = it.next().value; // console.log(q); const a = it.next("lager").value; // console.log(a); } // What is your favorite beer? // lager // No soup for you! { const it = favBeer(); const q = it.next().value; // console.log(q); const a = it.next("ipa").value; // console.log(a); } // What is your favorite been? // ipa // OK, soup.
Generators and promises
Now we can talk about how generators and promises form the basis of the async / await construction. Imagine that instead of returning certain values with the
yield
keyword, the generator returns promises. In this situation, the generator can be wrapped in a function that will wait for promise resolution and return the value of the promise to the generator in the
.next()
method, as shown in the previous example. There is a popular library,
co , which performs just such actions. It looks like this:
co(function* doStuff(){ var result - yield someAsyncMethod(); var another = yield anotherAsyncFunction(); });
Results
According to the author of this material, JS developers need to know how generators work, just to understand the features of the internal structure of the async / await structure. But you should not use them directly in your own code. Generators injected into JavaScript the ability to suspend the execution of the function and return to it when (and if) the developer deems it necessary. Until now, we, working with JS-functions, expected that they, being called, are simply executed from start to finish. The ability to suspend them is already something new, but this functionality is conveniently implemented in the async / await construction.
With this opinion, of course, it is possible to argue. For example, one of the arguments in favor of generators is that knowledge of how they work is useful for debugging code with async / await, since generators are hidden inside this construct. However, the author of the material believes that it is, nevertheless, something other than the use of generators in your own code.
Dear readers! What do you think about generators? Maybe you know some options for their use, which justify their direct use in the code of JS-projects?
Royal promo code for a 10% discount on our virtual servers:
