unhurried

コンピュータ関連ネタがほとんど、ときどき趣味も…

How to cope with Callback Hell #2 co generator

The second countermeasure of Callback Hell is using co. co is a library which utilizes Generator mechanism and make it easy to write serial and parallel flows.

const co = require('co');
const thunkify = require('thunkify')

// a function which has a callback in its argument
var callbackFunction = function(message, timeout, callback) {
    setTimeout(function() {
        console.log(message);
        callback(null, message);
    }, timeout);
};

// thunkify converts a function with a callback to one returning a thunk.
// yield can be applied to thunk in generator. (yeildable)
var thunkifiedFunction = thunkify(callbackFunction);

// a function which returns a Promise
var promiseFunction = function(message, timeout) {
    return new Promise(function(resolve, reject){
        setTimeout(function() {
            console.log(message);
            resolve(message);
        }, timeout);
    });
};

// When a Generator (function*(){...}) is passed to co,
// co will proceed the process of the Generator until a yeild statement appear,
// and resume the process after the Promise object is finished.
co(function*() {
    console.log('--- co start ---');
    
    // Process two functions in parallel.
    // Specify an array or an object to yeild.
    var response = yield [
        thunkifiedFunction('co two', 800),
        promiseFunction('co one', 600),
    ];
    console.log("response: " + JSON.stringify(response));

    // Process two functions serially.
    yield thunkifiedFunction('co three', 200);
    yield promiseFunction('co four', 400);

    // Functions defined using co.wrap can be used in co generator.
    yield wrappedFunction();

    console.log('--- co end ---');

    // When return is called, co.then() is executed.
    return "Generator finished.";

    // When a reject function is called in one of Promise objects, 
    // co.error() is executed.
    // yield Promise.reject("An error happened.");

}).then(function(value){
    console.log('--- then start ---');
    console.log(value);

}).catch((error) => {
    console.log('--- error start ---');
    console.log(error);
});

// Functions using co generator can be defined by using co.wrap.
// They are also yieldable because they also return a Promise object.
var wrappedFunction = co.wrap(function* () {
    console.log('--- co.wrap start ---');

    var response = yield [
        thunkifiedFunction('co.wrap two', 800),
        thunkifiedFunction('co.wrap one', 600),
    ];
    console.log("co.wrap response: " + JSON.stringify(response));
    yield thunkifiedFunction('co.wrap three', 200);
    yield thunkifiedFunction('co.wrap four', 400);

    console.log('--- co.wrap end ---');

    return yield Promise.resolve(null);
});

Result

$ node co_generator.js
--- co start ---
co one
co two
response: ["co two","co one"]
co three
co four
--- co.wrap start ---
co.wrap one
co.wrap two
co.wrap response: ["co.wrap two","co.wrap one"]
co.wrap three
co.wrap four
--- co.wrap end ---
--- co end ---
--- then start ---
Generator finished.