A function*
declaration creates a GeneratorFunction
object. Each time when a generator function is called, it returns a new Generator
object, which conforms to the iterator protocol. When the iterator's next()
method is called, the generator function's body is executed until the first yield
expression, which specifies the value to be returned from the iterator or, with yield*
, delegates to another generator function. The next()
method returns an object with a value
property containing the yielded value and a done
property which indicates whether the generator has yielded its last value, as a boolean. Calling the next()
method with an argument will resume the generator function execution, replacing the yield
expression where an execution was paused with the argument from next()
.
Generators in JavaScript — especially when combined with Promises — are a very powerful tool for asynchronous programming as they mitigate — if not entirely eliminate -- the problems with callbacks, such as Callback Hell and Inversion of Control. However, an even simpler solution to these problems can be achieved with async functions.
A return
statement in a generator, when executed, will make the generator finish (i.e. the done
property of the object returned by it will be set to true
). If a value is returned, it will be set as the value
property of the object returned by the generator. Much like a return
statement, an error thrown inside the generator will make the generator finished — unless caught within the generator's body. When a generator is finished, subsequent next()
calls will not execute any of that generator's code, they will just return an object of this form: {value: undefined, done: true}
.
function*
declarations behave similar to function
declarations — they are hoisted to the top of their scope and can be called anywhere in their scope, and they can be redeclared only in certain contexts.