JavaScriptでコールバックな関数をPromise化する

最近のJavaScriptのライブラリはasync/awaitに対応していることが多くなってきました。 おかげで、かなり短かいコードで非同期処理が実現出来るようになっています。

とはいえ、まだまだ非対応のライブラリもあり、混ざってしまった日にはコードが酷いことに…。 というわけで、コールバック関数を渡さないといけない古いスタイルのライブラリを、Promiseでラッピングしてasync/await出来るようにする方法です。

対応前のコード

対応前のコードのイメージは、以下のようなものです。 これを、Promiseを使って綺麗にしていきます。

database.get((data, error) => {
    if (error) {
        console.error(error);
        return;
    }

    database.set(data + 1, error => {
        if (error) {
            console.error(error);
        }
    });
});

データベースから何かの数値を持ってきて、値を加算して格納する、という雰囲気で書いてみたものです。 内容は単純ですが、コールバックが入れ子になってしまって見通しはかなり悪めです…。

Promise化するコード

先ほどのサンプルで使ったdatabase.getdatabase.setをPromiseで包むためには、以下のようなコードを書くことになります。

function getData() {
    return new Promise((resolve, reject) => {  // Promiseのコンストラクタを使って「resolve」と「reject」を作る
        database.get((data, error) => {
            if (!error) {
                resolve(data);  // 処理が終わったら、「resolve」に戻り値を渡す
            } else {
                reject(error);  // 処理に失敗したときは、「reject」にエラーの内容を渡す
            }
        });
    });
}

function setData(data) {
    return new Promise((resolve, reject) => {
        database.set(data, error => {
            if (!error) {
                resolve();  // 返すべき戻り値が無いのであれば、引数を渡さずに「resolve」を呼び出す
            } else {
                reject(error);
            }
        });
    });
}

対応後のコード

PromiseでラッピングしたgetDatasetDataを使うと、先ほどの長いコードは以下のように短く書くことが出来るようになります。

getData()
    .then(data => setData(data + 1))
    .catch(error => console.error(error));

async/awaitを使う場合は以下のようになります。こちらの方が直感的ですね。

try {
    const data = await getData();
    await setData(data + 1);
} catch(error) {
    console.error(error);
}