Asynchronous code is a core part of Javascript. Arguably, it is the most fundamental feature that took JS from a micro client side language, to a scripting language, to a fully featured server technology.

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.

With Promises:

You declare the control / flow of code outside the async blocks

a.then(b).then(c)

You declare the async “next step” inside the async blocks.

Promise.resolve(result)

With Generators:

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

generator.next()

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.

Tl;dr

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.)




Tagged with: