unhurried

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

Callback地獄対策 (2) co generator

Callback地獄対策の2つ目はcoについてまとめました。
coはGeneratorの仕組みを利用して簡単に並列・直列処理フローを記述できるようにするライブラリです。

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

// callbackを引数にとる一般的な関数
var callbackFunction = function(message, timeout, callback) {
    setTimeout(function() {
        console.log(message);
        callback(null, message);
    }, timeout);
};

// thunkifyはcallbackを引数にとる関数をthunkを返却する関数に変換する
// thunkにはgenerator内でyeild式を適用できる(yeildable)
var thunkifiedFunction = thunkify(callbackFunction);

// Promiseを返却する一般的な関数
var promiseFunction = function(message, timeout) {
    return new Promise(function(resolve, reject){
        setTimeout(function() {
            console.log(message);
            resolve(message);
        }, timeout);
    });
};

// co関数にGenerator(function*(){...})を渡すと、
// co関数はGeneratorの処理をyeild式まで進め、
// yeild式に指定されたPromiseの完了を待ってから処理を再開させる
co(function*() {
    console.log('--- co start ---');
    
    // 2つの関数を並列処理する
    // 並列処理をするにはyeildにArrayもしくはObjectを指定する
    var response = yield [
        thunkifiedFunction('co two', 800),
        promiseFunction('co one', 600),
    ];
    console.log("response: " + JSON.stringify(response));

    // 2つの関数を直列処理する
    yield thunkifiedFunction('co three', 200);
    yield promiseFunction('co four', 400);

    // co.wrapで定義した関数(後述)はco generator内で利用できる
    yield wrappedFunction();

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

    // Generatorでreturnするとco.then()に指定した関数が実行される
    return "Generator finished.";

    // いずれかのPromiseでreject関数が呼ばれると
    // co.error()に指定した関数が実行される
    // 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);
});

// co.wrapを使うことでco generatorを利用した関数を定義できる
// この関数もPromiseを返却するのでyeildableとなる
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);
});

実行結果

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