[Javascript] awaitは役に立つ

# 今さらだけどawait

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

# 本記事の内容

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

# awaitの使い方

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

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

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

# await は何ができるのか？

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

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

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

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

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

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

# await の使いどころ

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

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

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

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

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

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

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

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

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

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

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

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

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

```

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

```javascript:10秒後にログ出力(await)
console.log('START');
await sleep(10 * 1000);
console.log('10秒経過');
```

```javascript:sleep関数の実装例
function sleep(milliSeconds) {
    return new Promise((resolve) => {
        setTimeout(() => resolve(), milliSeconds);
    });
}
```

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

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

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

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

## ユーザーの入力を待つ

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

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

```javascript
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 文で判定するという、非常に分かりやすいコードにできます。

```javascript
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を簡単かつ綺麗に分離することができます。

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