Generator (ES6)-
Functions that can return multiple values at a different time interval, as per the user demands and can manage its internal state are generator functions. A function becomes a GeneratorFunction if it uses the function* syntax.
They are different from the normal function in the sense that normal function run to completion in a single execution whereas generator function can be paused and resumed, so they do run to completion but the trigger remains in our hand. They allow better execution control for asynchronous functionality but that does not mean they cannot be used as synchronous functionality.
Note: When a generator function is executed it returns a new Generator object.
The pause and resume are done using yield&next. So let's look at what are they and what they do.
Yield/Next-
The yield keyword pauses generator function execution and the value of the expression following the yield keyword is returned to the generator's caller. It can be thought of as a generator-based version of the return keyword.
The yield keyword actually returns an IteratorResult object with two properties, value and done. (Don’t know what are iterators and iterables then read here).
Once paused on a yield expression, the generator's code execution remains paused until the generator's next() method is called. Each time the generator's next() method is called, the generator resumes execution and returns the iterator result.
pheww..enough of theory, let's see an example
function* UUIDGenerator() {
let d, r;
while(true) {
yield 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
r = (new Date().getTime() + Math.random()*16)%16 | 0;
d = Math.floor(d/16);
return (c=='x' ? r : (r&0x3|0x8)).toString(16);
});
}
};
Here, UUIDGenerator is a generator function which calculates the UUID using current time a random number and returns us a new UUID every time its executed.
To run the above function we need to create a generator object on which we can call next().
const UUID = UUIDGenerator();
// UUID is our generator object
UUID.next()
// return {value: 'e35834ae-8694-4e16-8352-6d2368b3ccbf', done: false}
UUID.next() this will return you the new UUID on each UUID.next() undervalue key and done will always be false as we are in an infinite loop.
Note: We pause above the infinite loop, which is kind of cool and at any “stopping points” in a generator function, not only can they yield values to an external function, but they also can receive values from outside.
There is a lot of practical implementation of generators as one above and a lot of libraries that heavily use it, co, koa, and redux-saga are some examples.
Async/Await (ES7)
Traditionally, callbacks were passed and invoked when an asynchronous operation returned with data which are handled using Promise.
Async/Await is the special syntax to work with promises in a more comfortable fashion which is surprisingly easy to understand and use.
Async keyword is used to define an asynchronous function, which returns an AsyncFunction object.
Await keyword is used to pause async function execution until a Promise is fulfilled, that is resolved or rejected, and to resume execution of the async function after fulfillment. When resumed, the value of the await expression is that of the fulfilled Promise.
Key points:
- Await can only be used inside an async function.
- Functions with the async keyword will always return a promise.
- Multiple awaits will always run in sequential order under the same function.
- If a promise resolves normally, then await promise returns the result. But in case of rejection, it throws the error, just if there were a throw statement at that line.
- An async function cannot wait for multiple promises at the same time.
- Performance issues can occur if using await after await as many times one statement doesn’t depend on the previous one.
So far so good, now let's see a simple example:-
async function asyncFunction() {
const promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("i am resolved!"), 1000)
});
const result = await promise;
// wait till the promise resolves (*)
console.log(result); // "i am resolved!"
}
asyncFunction();
The asyncFunction execution “pauses” at the line await promise and resumes when the promise settles, with the result becoming its result. So the code above shows “i am resolved!” in one second.
Generator and Async-await — Comparison
Generator functions/yield and Async functions/await can both be used to write asynchronous code that “waits”, which means code that looks as if it was synchronous, even though it really is asynchronous.
A generator function is executed yield by yield i.e one yield-expression at a time by its iterator (the next method) whereas Async-await, they are executed sequential await by await.
Async/await makes it easier to implement a particular use case of Generators.
The return value of Generator is always {value: X, done: Boolean} whereas for Async function it will always be a promise that will either resolve to the value X or throw an error.
An async function can be decomposed into Generator and promise implementation which is good to know stuff.
Please consider entering your email here, if you’d like to be added to my email list and follow me on dev.to to read more article on javascript and on GitHub to see my crazy code.
If anything is not clear or you want to point out something, please comment down below.
Thanks :)
Top comments (1)
Thanks, this great. I wonder if you can show how async/await decomposes into yield/next. In fact I have read elsewhere that async/await is merely syntactic sugar for yield/next. But I have yet to find analogous presentations, that is an async/await sample rewritten and functionally equivalent to a yield/next sample.