Coroutines - jellyfish-tom/TIL GitHub Wiki

[SOURCES]

Coroutines understand that generators may be hard to predict and reason about in production code and remedy this situation by wrapping a generator and abstracting away all of the complexity.

Coroutines allow us to yield our asynchronous functions one line at a time, making our code look synchronous.

Nowadays (september 2018) mainly two libraries implementing coroutines are used.

  • Co
  • Bluebird

Let’s establish some basic rules to using coroutines:

  1. Any function to the right of a yield must return a Promise.
  2. If you want to execute your code now, use co.
  3. If you want to execute your code later, use co.wrap.
  4. Make sure to chain a .catch at the end of your coroutine to handle errors. Otherwise, you should wrap your code in a try/catch block.
  5. Bluebird’s Promise.coroutine is the equivalent to Co’s co.wrap and not the co function on it’s own.

Syntax:

import co from 'co';
app.post("/purchase", (req, res) => {
    co(function* () {
        const person = yield user.findOneAsync(req.body);
        const permissions = yield permissions.findAllAsync(person);
        if (isAllowed(permissions)) {
            const confirmNum = yield transaction.processAsync(user);
            res.send("Your transaction was successful!")
        }
    }).catch(err => handleError(err))
});

Note that multiple processes can be run concurrently with single coroutine. Result of it just needs to be destructurized.


import co from 'co';
// with objects
co(function*() {
    const {user1, user2, user3} = yield {
        user1: user.findOneAsync({name: "Will"}),
        user2: user.findOneAsync({name: "Adam"}),
        user3: user.findOneAsync({name: "Ben"})
    };
).catch(err => handleError(err))

// with arrays
co(function*() {
    const [user1, user2, user3] = yield [
        user.findOneAsync({name: "Will"}),
        user.findOneAsync({name: "Adam"}),
        user.findOneAsync({name: "Ben"})
    ];
).catch(err => handleError(err))

and with Bluebird library:

import {props, all, coroutine} from 'bluebird';

// with objects
coroutine(function*() {
    const {user1, user2, user3} = yield props({
        user1: user.findOneAsync({name: "Will"}),
        user2: user.findOneAsync({name: "Adam"}),
        user3: user.findOneAsync({name: "Ben"})
    });
)().catch(err => handleError(err))

// with arrays
coroutine(function*() {
    const [user1, user2, user3] = yield all([
        user.findOneAsync({name: "Will"}),
        user.findOneAsync({name: "Adam"}),
        user.findOneAsync({name: "Ben"})
    ]);
)().catch(err => handleError(err))