unhurried

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

Callback地獄対策 (3) aa generator

今回は、Qiitaで人気のあるCallback地獄対策の記事で紹介されているaaについて調べてみました。

aaもcoと同様にGeneratorの仕組みを利用して簡単に並列・直列処理フローを記述できるようにするライブラリです。 aaはcoと比べるとthunkify/promisifyが同じライブラリにパッケージされていることと、thunkifyAll/promisifyAllでオブジェクト全体にかけれられるの良いところと思います。 一方で、co.wrapに相当する機能はなく、並列・直列処理フローを利用したyieldableな関数を作るときに少し不便と感じました。

const aa = require('aa');

// callbackを引数にとる関数を持つオブジェクト
var callbackObject = {
    func: function(message, timeout, callback) {
        setTimeout(function() {
            console.log(message);
            callback(null, message);
        }, timeout);
    }
};

// aa.promisifyAllはオブジェクト全体に対してpromisifyを適用する
// promisifyはcallbackを引数にとる関数をPromiseを返却する関数に変換する
// Promiseoにはgenerator内でyeild式を適用できる(yeildable)
var promisifiedObject = aa.promisifyAll(callbackObject);

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

    // 2つの関数を直列処理する
    yield promisifiedObject.funcAsync('aa three', 200);
    yield promisifiedObject.funcAsync('aa four', 400);
    
    // aaを利用した関数の呼び出し(後述)
    yield wrappedFunction();

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

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

    // いずれかのPromiseでreject関数が呼ばれると
    // aa.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);
});

// aaにはco.wrapに相当する機能がないため、
// yieldを利用した関数を定義するにはaa(function*(){})を返却する関数を作る
var wrappedFunction = function() {
    return aa(function* () {
        console.log('--- wrap start ---');

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

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

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

実行結果

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

参考