[Javascript] async, await を使った非同期処理入門まとめ

[Javascript] async, await を使った非同期処理入門まとめ

async, await とは

Javascript における async, await とは、Promise を使った非同期処理を暗黙的に扱うことで、同期的なコードのような書き方で非同期処理を記述することができるようにするための構文です。

Javascript における非同期処理は Promise を使うことで、コールバック地獄を回避できるようになりましたが、代わりに Promise オブジェクトを生成しなければなりません。メソッドチェーンを使っていい感じに記述できるようになりましたが、これをさらに簡潔に書けるようにするために async, await の構文を使うことができます。

async, await は見えないところで Promise を使っているので、まずは Promise についてしっかりと理解してから async, await に進みましょう。

MDNには次のようにあります。

async/await 関数の目的は、 promise を同期的に使用する動作を簡素化し、 Promise のグループに対して何らかの動作を実行することです。 Promise が構造化コールバックに似ているのと同様に、 async/await はジェネレータと promise を組み合わせたものに似ています。

async

async は、関数定義の頭につけることでその関数を実行する Promise を返します。この関数を async関数 と呼びます。async関数は return値にかかわらず、必ず Promise を返します。

async function asyncFunction(x) {
    return x;
}

function promiseFunction(x) {
    return new Promise(function(resolve, reject) {
        try {
            resolve(x);
        } catch (error) {
            reject(error);
        }
    });
}

上のサンプルで定義している2つの関数は同じような意味になります。async を付けた関数は Promise を返し、その戻り値を Promise が解決した際(resolve)の値とします。また、関数内部でエラーが発生した際にはそのエラーを reject します。

実態はPromiseを返しているのですが、Promiseオブジェクト生成の記述が簡略化され、タイプ数もネスト数も減っていることがわかります。

また、async関数の内部では await を使うことができます。

await

await は Promise の解決を待ち、その値を取り出してくれます。Promise が reject された場合にはその値をスローします。

// 2秒後に1加算
function promiseIncrement(num) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve(num + 1);
        }, 2000);
    });
}

async function asyncFunction(x) {
    // await で Promise の解決を待つ
    const n = await promiseIncrement(0);
    console.log(n); // Promiseオブジェクトではなく 1 になる
}

asyncFunction();
console.log('console end ...');

// console end ...
// 1

上記例では async関数内で await を使っています。Promise が解決するまで async関数内では await の位置で処理が止まることが確認できます。Promiseが解決された後に次の処理に移るので、n には処理結果の 1 が入っていることが確認できます(await がついていないと Promise オブジェクトがそのまま入っています)。

実際にすべての処理が止まるわけではなく、あくまで async関数内の await 部分で止まって見えるだけです。async関数自体は Promise オブジェクトを返すので、すぐに次の処理に移り、最後のコンソール出力は当然すぐに実行されます。

async, await で並列処理を実行する

複数のPromiseを並列で実行する場合、すべてを起動してから await しないと逐次実行されて並列にならないので注意する必要があります。

以下の例では、2秒かかる処理を3つ実行する例です。都度 await をつけて実行すると6秒かかりますが、すべての Promise を起動してから それぞれ await することで並列で動いてくれるので 2秒で処理が完了します。

// 2秒後に値を返す
function getNum(num) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(num);
        }, 2000);
    });
}

// 6秒後に出力
async function f1() {
    const n1 = await getNum(1);
    const n2 = await getNum(2);
    const n3 = await getNum(3);
    console.log(n1, n2, n3);
}

// 2秒後に出力
async function f2() {
    const p1 = getNum(1);
    const p2 = getNum(2);
    const p3 = getNum(3);
    const n1 = await p1;
    const n2 = await p2;
    const n3 = await p3;
    console.log(n1, n2, n3);
}

まとめ

  • async は関数宣言で使うキーワード
  • async関数は戻り値にかかわらず Promise を返す
  • await はasync関数内で使うキーワード
  • awaitPromise の解決を待機して値を取得する

以上。

Javascriptカテゴリの最新記事