【JavaScript】async/awaitとPromiseの違い

### この記事について
コメントにて、より良い構文をいただきました。記事の最後に記載いたします。また、~~より高度な~~普通の構文で書かれたasync/awaitもコメントにて記載いただきましたので、記事の最後に記載いたしました。どのような出力順になるか考えるのが楽しく大変勉強になりました。ありがとうございました。

追記：コメントにてthenとasync/awaitを一緒に利用することはないとご指摘・解説いただきました。大変分かりやすく、非同期処理の不明点が払拭されました。ありがとうございます。  
以下にコメントにて記載いただいた「Promiseとasync/await」の違いを載せました。
> await と .then を一緒に使うことはしないと思います。

> async関数
>・自動的に Promise が作られて、関数の復帰値は Promise になる
>・関数内で return したら、return値を引数にして Promise の resolve が自動的に呼び出される
>・関数内で例外が発生したら、Promise の reject が自動的に呼び出される
>await
>・Promise の resolve か reject が呼ばれるまで待ち状態になる
>・Promise の resolve が呼ばれたら、resolveの引数が await の 戻り値 になって処理が再開する
>・Promise の reject が呼ばれたら、例外が発生する

# async/awaitとPromiseの違い（誤っている構文）
<font color="Red">※追記：以下の【async/awaitとPromiseの違い】は誤った構文の記載になりますが、「誤っている構文の例」として残しておきます。</font>

Async/awaitはAsync関数内であれば、同期・非同期処理を一緒に記述して、手続き型の処理を実現できます。Promiseは、同期・非同期処理を一緒に記述した場合、thenでつないだり処理の前後を完璧に把握する必要があります。  
以下はPromiseを使った、同期・非同期処理を一緒に記述した場合です。

```javascript
function test() {
  console.log("関数開始");
  takeLongTimeFn().then((a) => console.log(a));
  function takeLongTimeFn() {
    return new Promise((resolve, reject) => {
      setTimeout(function () {
        resolve("成功");
      }, 2000);
    });
  }
  console.log("関数終了");
}

test();

/*
関数開始
関数終了
成功
*/
```
最後のコメントアウトした部分が実際の出力結果です。このようにPromiseを用いると手続き型ではなくなり非同期で処理が走ります。それを解決するために以下のように記載の順序を書き換えます。

```javascript
function test() {
  console.log("関数開始");
  takeLongTimeFn().then((a) => {
    console.log(a);
    console.log("関数終了");
  });
  function takeLongTimeFn() {
    return new Promise((resolve, reject) => {
      setTimeout(function () {
        resolve("成功");
      }, 2000);
    });
  }
}

test();
/*
関数開始
成功
関数終了
*/
```
このように記載すれば関数開始→成功→関数終了の順でログが出力されます。しかし見ての通り出力の順番が手続き型となっていないため、読みづらいです。そこでasync/awaitを利用します。同じ処理をasync/awaitで記載したものが以下になります。  

```javascript
async function test() {
  console.log("関数開始");
  await takeLongTimeFn().then((a) => {
    console.log(a);
  });
  function takeLongTimeFn() {
    return new Promise((resolve, reject) => {
      setTimeout(function () {
        resolve("成功");
      }, 2000);
    });
  }
  console.log("関数終了");
}

test();
/*
関数開始
成功
関数終了
*/
```
このように、同期・非同期処理を一緒に記述している関数の前に、asyncを付けることでその関数内で実行される非同期関数にawaitを付けることができます。awaitが付けられた関数が実行されるとその関数はPromiseが返ってくるまで次の処理に進むことがありません。

# 同様の処理で可読性の高い構文（コメント参照）
```js
async function test() {
  console.log("関数開始");
  let message = await takeLongTimeFn()
  console.log(message);
  console.log("関数終了");
}

function takeLongTimeFn() {
  return new Promise(resolve => setTimeout(() => resolve("成功"), 2000));
}

test();
/*
関数開始
成功
関数終了
*/
```

# 普通のasync/await構文
```js
async function takeLongTimeFn(type) {
  await new Promise(resolve => setTimeout(resolve, 2000));
  return type + "成功";
}

function synctest() {
  console.log("sync関数開始");
  takeLongTimeFn("sync").then(message => {
    console.log(message);
    console.log("sync関数then終了");
  });
  console.log("sync関数終了");
}

async function asynctest() {
  console.log("async関数開始");
  let message = await takeLongTimeFn("async")
  console.log(message);
  console.log("async関数終了");
}

synctest();
asynctest();
/*
sync関数開始
sync関数終了
async関数開始
sync成功
sync関数then終了
async成功
async関数終了
*/
```