Generators still work on the same call stack, performed by normal functions. There are no multiple stacks that are evaluated between them.
When you instantiate the generator (by calling the generator function) and then call its .next() method, it just calls that call at the top of the stack. Then it will run the code inside the generator function.
When it encounters a yield , it simply pops the call from the stack and returns from the .next() method, continuing, as usual, after calling any function.
The difference between a generator call and a regular function call is what happens when you enter and exit the code.
The normal function remains at the end of the function body or in the return / throw expression, then it ends. The generator also goes to yield , but it must remember the state (basically by storing the instruction pointer in the generator instance) so that it can resume execution after yield . He should also remember the state of all local variables, but the engines already know how to do this from the implementation of closures.
A normal function enters a call by setting up a new environment and starting execution in the upper body of the function. A call to the generator will restore state so that it can continue to work where it left off.
The normal behavior of the stack does not affect this.
Ok, I lied. yield* makes things a little more complicated. The recursive chain of yield* ed generators will need to be pressed and placed a few frames of the stack when the .next() call .next() in or out. The engine can optimize this context switch using multiple stacks. However, one could see them stacked on top of each other, forming one large stack, and at runtime only the top of this separate stack is managed.
Bergi source share