💪

callbackの関数をawait化する

2021/11/30に公開

この記事はJavaScriptカレンダー2021 1日目の記事です。過疎っているので1日目getしちゃいました。

Github: https://github.com/bluepost59/callback_to_await

Promise

Promiseとはcallbackを回避するために実装されている機能です。Promiseオブジェクトは言葉にするのが難しいのですが、日本語にすると「お約束」という意味です。「この処理が終わったら次はこうする」というようなお約束を作っておくことで、非同期処理を見通しよく書けるようになります。

const mypromise = new Promise(
    (resolve, reject) => {
        if (err) reject(err);
        resolve()
    }
);

Promiseを使うときは、まずコンストラクタにresolverejectを引数に持つ関数を与えてPromiseオブジェクトを作ります。この2つはともに関数で仮引数のようなもので、resolveには処理が問題なく終了したときに実行される処理、rejectにはエラーが生じたときの処理を代表させます。

本題で詳しく説明しますが、await句をつけることでPromiseを解決することができます。これによってcallback地獄に陥ることなく逐次処理に近い形で記述できます。

Promiseでcallbackをawaitで見通しよく書く

ここからが本題です。既存の関数でcallbackを使う仕様になっているものをうまくラップして、awaitで処理できるようにします。

今回は簡単のためにsetTimeoutをawaitで逐次実行のように記述できるようにします。

// 1秒経ったら"finished"と表示する
setTimeout(() => {
    console.log("finished");
}, 1000);

引数や戻り値が不要な場合

引数や戻り値が不要な場合は、次のようにPromiseで包むことでそのままawaitで実行できるようになります。

const timeout_promise = new Promise(
    (resolve, reject) => {
        setTimeout(resolve, 1000);
    }
);

// 実行パート
// Promiseなので()をつけない
async function main(){
    await timeout_promise;
    console.log("finished");
}

awaitはasync function内でしか使うことができません。これはトップレベルも含んでいて、ベタにawaitを使おうとするとエラーが出るので注意しましょう。

引数や戻り値を取る場合

一番実践的なパターンかと思います。引数や戻り値を取る場合、builderとなる関数を用意してPromiseを返すようにします。

function timeout_function(msg) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(msg);
        }, 1000);
    });
}

// 実行パート
async function main(){
    // awaitで逐次実行のように記述できる
    res = await timeout_function("finished");
    console.log(res);
}

まとめ

JavaScriptのPromiseという仕様は他の言語がメインの人にはわかりにくいと思います。自分もまとめるまでよくわかっていませんでした。ただ使い方を覚えればcallbackと段違いにわかりやすいコードを書くことができるので、積極的に使っていきましょう。

Discussion