SetTimeoutは、async/await関数では使えない

# はじめに
JavaScriptの非同期処理を学び概念を理解してきたが、Promiseを作る側の関数（Promiseを返す関数）について疑問があったため、事象と結論を整理してみた。

**【疑問】以下の2つの関数になぜ動作の違いが発生するのか？**
①Promiseを返す関数として、SetTimeoutを「new Promise()（promiseのコンストラクター）」で定義した場合は、SetTimeoutの時間を待機した後Promiseを返す（これは検索するとよく出てくる一般的な内容）。

②Promiseを返す関数として、SetTimeoutを「async」のキーワードで括る定義をした場合は、setTimeoutの時間を待機せずにPromiseを返す。

この疑問を抱いた理由は、②のasyncで括ることでPromiseを返すことができるため、呼出し元（②の関数を呼び出す側の関数）で async/awaitを指定することでsetTimeoutの処理が待機されるのではと思ったが、期待する結果にはならなかった（待機されなかった）ため、疑問を持った。実際に動かしながら紐解いていく。

## まずは2つの処理結果を比較してみる
**①SetTimeoutを「new Promise()（promiseのコンストラクター）」で定義してPromiseを返す関数**
```javascript
// Promiseを返す関数
const delayedColorChange = (color, delay) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            document.body.style.backgroundColor = color;
            console.log(`${color}に変えました。`);
            resolve();
        }, delay);
    });
};


//Promise呼出し元の関数
async function rainbow() {
    await delayedColorChange('red', 1000);
    await delayedColorChange('Orange', 1000);
    await delayedColorChange('yellow', 1000);
    await delayedColorChange('green', 1000);
    await delayedColorChange('blue', 1000);
    await delayedColorChange('indigo', 1000);
    await delayedColorChange('violet', 1000);
};

console.log('開始');
rainbow()
    .then(() => console.log('終了'))
    .catch((e) => console.log(e));
```
処理結果は、1秒毎に画面の色が変わっていき、想定通りSetTimeoutが待機される。

<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="zYjRQMV" data-user="Tminato" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/Tminato/pen/zYjRQMV">
  new promise</a> by minato (<a href="https://codepen.io/Tminato">@Tminato</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>



**②SetTimeoutを「async」のキーワードで括る定義をしてpromiseを返す関数**
```javascript
// Promise関数をAsync関数で定義
async function delayedColorChange(color, delay) {
    setTimeout(() => {
        document.body.style.backgroundColor = color;
        console.log(`${color}に変えました。`);
    }, delay)

    return 'aaa'
};

//Promise呼出し元の関数
async function rainbow() {
    await delayedColorChange('red', 1000);
    await delayedColorChange('Orange', 1000);
    await delayedColorChange('yellow', 1000);
    await delayedColorChange('green', 1000);
    await delayedColorChange('blue', 1000);
    await delayedColorChange('indigo', 1000);
    await delayedColorChange('violet', 1000);
};

console.log('開始');
rainbow()
    .then(() => console.log('終了'))
    .catch((e) => console.log(e));

```

処理結果は、①とは異なり同時に1秒後に全ての色が変わってしまい、結果として最後に設定した色になってしまう。

<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="eYrVPqw" data-user="Tminato" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/Tminato/pen/eYrVPqw">
  Promise</a> by minato (<a href="https://codepen.io/Tminato">@Tminato</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>




## 動作の違いを調べてみる。
### ①SetTimeoutを「new Promise()（promiseのコンストラクター）」で定義してPromiseを返す関数
公式のドキュメントでPromiseの、コンストラクターを調べてみるとこんな記載があった。

>executor については、以下のことを理解することが重要です。
>
>・executor の返値は無視されます。
>・executor の中でエラーが発生した場合、プロミスは拒否されます。
>
>つまり、 executor の中のコードが効果を発揮する仕組みは、次のようなものです。
>
>・コンストラクターが新しい Promise オブジェクトを生成した時点で、対応する resolutionFunc と rejectionFunc の一対の関数も生成されます。これらは Promise オブジェクトに「結束」されます。
>・executor 内のコードは、何らかの操作を行う機会を得、その結果を（値が他の Promise オブジェクトでない場合）「解決済み」または「拒否済み」として反映し、それぞれ resolutionFunc または rejectionFunc を呼び出して終了します。
>・つまり、executor 内のコードは、 resolutionFunc や rejectionFunc による副次的影響を介して通信を行います。その副次的影響とは、 Promise オブジェクトが「解決済み」または「拒否済み」になることです。

:link: https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise

今回の①のコードの場合は、変数を以下のように読み替えることができ、もう少し簡潔に書くと
・executor　→　setTimeout
・resolutionFun　→　resolve
・rejectionFunc　→　reject

**「promiseのコンストラクターはpromiseの生成時にsetTimeoutを実行し、そのsetTimeout内でresolveもしくはrejectが呼び出された時点でコンストラクターが終了する。」**
と解釈することがでる。

そのため、setTimeoutが終了するまで、promiseのコンストラクターが終了するこはなく、promiseが戻らないため、結果として待機されるということにる。
検証として、①のソースで「resolve()」をコメントアウトして実行してみると、1つ目の赤色に変更するところで止まってしまい、Promiseのコンストラクターが終了していないことがわかる。

```javascript
// Promiseを返す関数
const delayedColorChange = (color, delay) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            document.body.style.backgroundColor = color;
            console.log(`${color}に変えました。`);
            // resolve();
        }, delay);
    });
};


//Promise呼出し元の関数
async function rainbow() {
    await delayedColorChange('red', 1000);
    await delayedColorChange('Orange', 1000);
    await delayedColorChange('yellow', 1000);
    await delayedColorChange('green', 1000);
    await delayedColorChange('blue', 1000);
    await delayedColorChange('indigo', 1000);
    await delayedColorChange('violet', 1000);
};

console.log('開始');
rainbow()
    .then(() => console.log('終了'))
    .catch((e) => console.log(e));
```
<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="NWMyeMN" data-user="Tminato" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/Tminato/pen/NWMyeMN">
  promise resolve</a> by minato (<a href="https://codepen.io/Tminato">@Tminato</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

### ②SetTimeoutを「async」のキーワードで括る定義をしてpromiseを返す関数
こちらについては、下記のリンク先の記事を参考にさせていただいた。
:link: https://qiita.com/soarflat/items/1a9613e023200bbebcb3

returnされているときに　promiseがresolve　として戻り、
throw new Error()が行われたときに、rejectが戻ることがわかる。

そのため、
**asyncのdelayedColorChange関数を呼び出した際に、setTimeoutの処理は実行されているが、setTimeoutの処理が完了するかを待つことはなく、次の処理のreturnに移ってpromiseのresolveが戻ることになる。**
その結果、setTimeoutが7色分同時に処理され、最後の色のみに画面が変る結果になったということが分る。

## SetTimeoutをasync/awaitで待機はできない...
前述で、asnycでsetTimeoutの処理を待たずに、promiseを返す関数に違いがあることが分かった。
setTimeoutを待機するために、　awaitキーワードをつけてあげれば待機できるのではと考え、これを試してみる。

```javascript
// Promise関数をAsync関数で定義
async function delayedColorChange(color, delay) {
    await setTimeout(() => {
        document.body.style.backgroundColor = color;
        console.log(`${color}に変えました。`);
    }, delay)

    return 'aaa'
};

//Promise呼出し元の関数
async function rainbow() {
    await delayedColorChange('red', 1000);
    await delayedColorChange('Orange', 1000);
    await delayedColorChange('yellow', 1000);
    await delayedColorChange('green', 1000);
    await delayedColorChange('blue', 1000);
    await delayedColorChange('indigo', 1000);
    await delayedColorChange('violet', 1000);
};

console.log('開始');
rainbow()
    .then(() => console.log('終了'))
    .catch((e) => console.log(e));

```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2830661/2cc16ee0-c3bf-304a-4d61-9e3a747459c1.png)
**結果は意味なし...**
これは、awaitすることができるのは、promiseを返す関数のみであり、setTimeoutは非同期処理であるが、promiseを返さないためawaitを利用することができない。

そうすると、awaitを記載するには、setTimeoutをpromiseを返す関数にする必要がり、以下のようなコードとなる。
```javascript
// Promis関数をAsync/awaitで定義
async function delayedColorChange(color, delay) {
    const p1 = new Promise((resolve, reject) => {
        setTimeout(() => {
            document.body.style.backgroundColor = color;
            console.log(`${color}に変えました。`);
            resolve();
        }, delay)
    })
    return await p1
};

//Promise呼出し元の関数
async function rainbow() {
    await delayedColorChange('red', 1000);
    await delayedColorChange('Orange', 1000);
    await delayedColorChange('yellow', 1000);
    await delayedColorChange('green', 1000);
    await delayedColorChange('blue', 1000);
    await delayedColorChange('indigo', 1000);
    await delayedColorChange('violet', 1000);
};

console.log('開始');
rainbow()
    .then(() => console.log('終了'))
    .catch((e) => console.log(e));
```
<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="wvjyNoJ" data-user="Tminato" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/Tminato/pen/wvjyNoJ">
  Primise async awit</a> by minato (<a href="https://codepen.io/Tminato">@Tminato</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

1秒ごとに色が変わり、想定する結果が得られることが分かる。
ただし、これは結局のところ、①SetTimeoutを「new Promise()（promiseのコンストラクター）」で定義したものに対して
awaitをかけているだけであり、①のSetTimeoutを「new Promise()（promiseのコンストラクター）」で定義してPromiseを返す関数と同じであると考えられる。

## まとめ
ということで、結論。
promiseを返す関数を定義する際に、setTimeout（非同期処理であるがpromiseを返さない関数）については、promiseのコンストラクターで定義することで処理を待機させることはできる。一方でasyncで定義した関数の場合は、setTimeout処理をasync関数内で待機させることができないということが分かった。理由は、setTimeoutがpromiseを戻り値としないため、async関数内でawaitすることができず、setTimeoutの関数の実行のみされて次の処理（return）へ進んでしまうためだ。結果として、呼び出し元でawaitをかけたところで、promiseを返す関数自体はsetTimeoutを実行のみして終了してしまうため、setTimeoutの待機時間に関係なく処理が進んでしまうという結論に至った。