11
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[Javascript] awaitは役に立つ

Last updated at Posted at 2022-06-13

今さらだけどawait

awaitは2017年あたりに出てきた機能で新しくもないですが、私は新しい機能が出ても大体の環境にサポートされるまで触らない意識低い系エンジニアなので、IEで使えない async/awaitとPromiseは存在を知りつつもスルーしてきたのですが、そろそろIEを切ってもいいと思えたので、解禁してみました。

本記事の内容

この記事はawaitがどんな時に役立つかを紹介するものです。
Promiseのことは知っている前提の記事なので、Promiseの使い方を知らない人は、まずそちらを調べてから読むのがおすすめです。

awaitの使い方

awaitの使い方について簡単におさらいです。
awaitはasyncをつけた非同期関数の中で使え、Promiseを返す関数呼び出しの前に書きます。

async function main() {
   await asyncFunction();
}

本記事でawaitを使っているコードは、明示していなくても全て非同期関数内の前提です。

await は何ができるのか?

awaitは非同期処理が終わるまで待つ機能で、awaitをつけると、Promiseの処理が終わるまで待って、終わった後に次の行に進みます。
そして、Promiseがresolveする値を、同期関数の戻り値のように扱うことができます。

例として、fetch関数でリソースを取得して、レスポンスのHTTPステータスを出力するコードを考えると、awaitなしでは以下のようになります。

fetch(resource).then(response => {
   console.log(response.status);
});

これが、awaitを使うと以下のように書けます。

const response = await fetch(resource);
console.log(response.status);

もう一つのポイントとしては、awaitの待機はUIスレッドをブロックせず、CPUリソースを無駄に食うこともありません(たぶん)

await の使いどころ

ここから具体的にawaitがどういうところで役立つか、例を挙げていきたいと思います。

非同期処理を順番に実行する

おそらく一番基本的で分かりやすいawaitの使い道だと思います。

Promiseを返す関数 a、b、cがあるとして、これを順番に実行する場合、awaitを使わないと以下のようになります。

a().then(() => {
   return b();
}).then(() => {
   return c();
}).then(() => {
   console.log('end');
})

ごちゃっとしていて、処理の流れがぱっと見で分かりづらいですね。
このコードが、awaitを使うと以下のようにとても見やすくなります。

await a();
await b();
await c();
console.log('end');

非同期処理の結果を順番に使う

上の例の派生系ですが、awaitを使うと非同期処理の実行結果も、同期処理的のような書き方で使えるようになります。

const resultA = await a();
const resultB = await b(resultA);
const resultC = await c(resultB);
console.log(resultC);

一定時間処理を止めるsleep関数

例えば、開始から10秒後にログ出力したい場合、awaitなしでは以下のようにsetTimeoutなどを使って、待機後の処理を別関数に切り出す必要があります。

10秒後にログ出力(setTimeout)
console.log('START');
setTimeout(() => {
   console.log('10秒経過');
}, 10 * 1000);

awaitを使えば、以下のようなsleep関数を実現することができます。

10秒後にログ出力(await)
console.log('START');
await sleep(10 * 1000);
console.log('10秒経過');
sleep関数の実装例
function sleep(milliSeconds) {
    return new Promise((resolve) => {
        setTimeout(() => resolve(), milliSeconds);
    });
}

通信エラーが起きたら1分待って再通信する

先に書いたsleep関数を使うと、通信が成功するまで1分間隔でリトライし続けるというコードがこんなにシンプルに書けます。

while (true) {
    try {
        return await fetch(resource);
    } catch (e) {
        await sleep(60);
    }
}

このように、awaitを使うと、非同期処理をwhileやforやifなどの制御文で制御できるようになります。

ユーザーの入力を待つ

await はユーザーの入力も待つことができます。
この使い方を思いついたときは、自分天才か?!と思いましたが、ググってみると普通にネットにいっぱい例があります(笑)
awaitで一番便利に感じたところで、この記事を書くモチベーションになりました。

例えば、ダイアログでYESかNOを選んで処理を分岐させたい場合、awaitなしならコールバック関数などを使う必要があります。
やり方は色々あると思いますが、例えばこんな感じです。

function main() {
    const yes = () => {
       // yesの処理
    };
    
    const no = () => {
       // noの処理
    };
    
    selectYesNo({yes, no});
}

function selectYesNo(callbacks) {
   yesButton.onclick = () => callbacks.yes();
   noButton.onclick = () => callbacks.no();
   // ダイアログ表示処理(省略)
}

これが、awaitを使うことによりYES/NOダイアログの結果を得るまで待ってif 文で判定するという、非常に分かりやすいコードにできます。

async function main() {
    const answer = await selectYesNo();
    
    if (result === 'yes') {
        // yesの処理
    } else if (result === 'no') {
        // noの処理
    }
}

function selectYesNo() {
    // ダイアログ表示処理(省略)
    return new Promise(resolve => {
       yesButton.onclick = () => resolve('yes');
       noButton.onclick = () => resolve('no');
    });
}

コールバック関数などを組み合わせて実装する必要があった、ユーザー入力を挟む一連の処理を、awaitを使えばで、一つの関数スコープで順序立てて書くことができるようになるります。

awaitでユーザー入力を待つと、簡単かつ綺麗にロジックとUIの分離ができる

awaitでユーザー入力を待つと、ロジックとUIを簡単かつ綺麗に分離することができます。

async function logic(selectYesNo) {
   const answer = await selectYesNo();

    if (answer === 'yes') {
        // yesの処理
    } else if (answer === 'no') {
        // noの処理
    }
}

ここで引数の selectYesNo はユーザーにYesかNoかを選ばせる非同期関数を想定しています。
このような非同期関数を引数に取ると、logic関数はどのようなUIでユーザーが選ぶのか知る必要がなく、その結果だけを使って処理を進めることができます。

selectYesNoがダイアログ入力だろうが、コマンド入力だろうが、どんなUIだろうがlogic関数側には関係がなくなります。

await のメリットとは

まとめると、awaitのメリットはこんな感じでしょうか。

  • 非同期処理を実行順に書ける
  • 非同期処理の結果が同期処理のように使える
  • 非同期処理をif、while、forなどの制御文で制御できる
  • なんでもいつまでも待てる。ユーザーの入力も待てる

awaitを使うと、コールバック関数などを組み合わせて書く必要があった複雑な処理を、シンプルに書けるようになります。

11
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?