In modern JS, Generators, Coroutines, and Promises are fundamental tools for writing async code, and avoiding “callback hell”. But for folks inexperienced with Generators, Coroutines, and Promises, it’s sometimes hard to know when to use which.
Generators and Promises are basically cousins, with inverted control of execution.
You declare the control / flow of code outside the async blocks
You declare the async “next step” inside the async blocks.
You declare the control / flow of code inside the async blocks.
yield a; yield b; yield c
You declare the async “next step” outside the async blocks
Inversion of Control
The important phrase here is inversion of control. Promises and Generators can accomplish the same stuff, and are largely interchangeable.
.then() is to resolve() as next() is to yield
What makes it harder is that… 99.999% of the time, Generators are used in parallel with a Coroutine library, like co. Coroutines will wrap our generators and promises into a single yieldable, which means less coding for us.
Co and Coroutines tidy all the next() calls we need for our Generators, so we, the developers, never have to write them explicitly. When working with co, having that next() call hidden from us makes it harder to see the parallels between Promises and Generators.
But, having to explicitly write .next() over and over (or in loops) is tedious, so we use co.
When to use Generators: When you’re working somewhere where you should own flow/control, but not the IO (e.g. controllers, routers)
When to use Promises: When you’re working somewhere where you should own the IO, but not the flow/control (e.g. models, services, etc.)