【async/await版】JavaScriptでループ中にスリープしたい。それも読みやすいコードで

[JavaScriptでループ中にスリープしたい。それも読みやすいコードで](https://qiita.com/akyao/items/a718cc78436df68d7e15)という5年くらい前の記事を読み、コメント欄に、今だったらasync/awaitを使ってこうできるよーという例が挙げあられていたんですが、パッと見て理解できなかったのでメモとして残しておきます。

理解するのに役立ったのは、JavaScript Promiseの本の[1.2.1. Promise workflow](https://azu.github.io/promises-book/#promises-overview)、[5.3. await式](https://azu.github.io/promises-book/#__code_await_code_%E5%BC%8F)です。
setTimeoutの挙動をgifで説明している[♻️ JavaScript Visualized: Event Loop](https://dev.to/lydiahallie/javascript-visualized-event-loop-3dif)も参考になりました。

今回は、1000ミリ秒ごとにランダムな数字を出力する処理を10回繰り返すコードを目標とします。

以下は、動かないけど実現したいコードです。

```javascript

for (let i = 0; i < 10; i++) {
    console.log(Math.random())
    sleep(1000);
}
```

## async/awaitでやってみる

上のコードは、以下のように async/await を用いて実現できます。

```javascript
function sleep(milliseconds) {
  return new Promise(resolve => setTimeout(resolve, milliseconds));
}

async function main() {
  for (let i = 0; i < 10; i++) {
    console.log(Math.random());
    await sleep(1000);
  }
}

main()
```

#### ちょっとした解説
- await式では非同期処理を実行し完了するまで、次の行（次の文）を実行しない
=> **``sleep(1000)``の実行が終わる**までfor文の次の周期にはいかない(1000ミリ秒停止する)。

- ``sleep関数``はPromiseを返している
=> **``sleep(1000)``の実行が終わる**　=　PromiseがFulfilledになる　=　promiseオブジェクトがsetTimeoutでmilliseconds(1000ミリ秒)後にresolveされる

#### ``new Promise(resolve => setTimeout(resolve, milliseconds));`` ってなんだ？

違和感があったのは、await式の返り値がないことです。
例えば、以下のコードだとawait式の返り値は sccess となります。

```javascript
function sleep(milliseconds) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve("success")
        }, milliseconds)
    });
}

async function main() {
  for (let i = 0; i < 10; i++) {
    console.log(Math.random());
    let value = await sleep(1000);
    console.log(value) // sccess
  }
}

main()
```

しかし今回は時間を止めるだけなのでPromiseは値をresolveする必要はないため、ちょっと違和感のあるコードになっていました。

```javascript
function sleep(milliseconds) {
  return new Promise(resolve => setTimeout(resolve, milliseconds));
}

async function main() {
  for (var i = 0; i < 10; i++) {
    console.log(Math.random());
    let value = await sleep(1000);
    console.log(value) // undefined
  }
}

main()
```

## async/awaitを使わずPromiseだけでやってみる

特に意味はないですが勉強になりそうなので。

#### Promiseのthenを復習する
参考 [とほほのPromise入門](http://www.tohoho-web.com/ex/promise.html)

100の2倍を求める非同期関数の使用例です。

```javascript
function func1(data, callback) {
  setTimeout(() => {
    callback(data * 2);
  }, 1000);
}

function sample_callback() {
  func1(100, function(value) {
    console.log(value); //200
  });
}

sample_callback();
```

この時点でつまずきそうですが、関数そのものを引数にして渡してるだけです。
これをPromiseで書き換えると以下になります。

```javascript
function func2(data) {
  return new Promise(function(resolve) {
    setTimeout(function() {
      resolve(data * 2);
    }, 1000);
  });
}

function sample_promise() {
  func2(100).then(data => {
    console.log(data); // 200
  });
}

sample_promise();
```
Promiseが1000ミリ秒後に ``resolve(data * 2)`` と解決されて then の onFulfilled に設定された関数に ``data * 2`` という値を渡します。
**awaitの代わりにthenを使っているイメージ**を持っているのですがあってるでしょうか？？？


#### promise chain

以下のコードでは、promise chain をつなげて処理をしています。
returnされてきた ``func2(data)``に対してさらに then で処理することを繰り返しています。
このコードでは、一秒間隔で 200, 400, 800 が出力されます。

```javascript
function func2(data) {
  return new Promise(function(resolve) {
    setTimeout(() => {
      resolve(data * 2);
    }, 1000);
  });
}

function sample_promise3() {
  func2(100)
    .then(data => {
      console.log(data);
      return func2(data); // 200
    })
    .then(data => {
      console.log(data);
      return func2(data); // 400
    })
    .then(data => {
      console.log(data); // 800
    });
}

sample_promise3();
```



#### 本題

ここまで理解できたのでasync/awaitを使わずPromiseだけでやってみます。

```javascript

let myPromise = main(1000);

for (let i = 0; i < 3; i++) {
  myPromise = myPromise.then(num => {
    console.log(num);
    return main(1000);
  });
}

function main(milliseconds) {
  return new Promise(function(resolve) {
    setTimeout(function() {
      resolve(Math.random());
    }, milliseconds);
  });
}
```

for文の中身が少しややこしくて、


```javascript
for (var i = 0; i < 3; i++) {
  myPromise = myPromise.then(num => {
    console.log(num);
    return main(1000);
  });
}
```
は、以下と(ほぼ)同義です。

```javascript
myPromise
  .then(num => {
    console.log(num);
    return main(1000);
  })
  .then(num => {
    console.log(num);
    return main(1000);
  })
  .then(num => {
    console.log(num);
  });
```

もっと分かりやすくすると、

```javascript
for (var i = 0; i < 3; i++) {
  myPromise = myPromise.then(hoge)
}
```
は、以下と同義です。

```javascript

myPromise
  .then(hoge)
  .then(hoge)
  .then(hoge);
```

## まとめ

#### Promiseのみを使う

```javascript

let myPromise = main(1000);

for (let i = 0; i < 3; i++) {
  myPromise = myPromise.then(num => {
    console.log(num);
    return main(1000);
  });
}

function main(milliseconds) {
  return new Promise(function(resolve) {
    setTimeout(() => {
      resolve(Math.random());
    }, milliseconds);
  });
}
```

#### async/awaitを使う

```javascript
function sleep(milliseconds) {
  return new Promise(resolve => setTimeout(resolve, milliseconds));
}

async function main() {
  for (let i = 0; i < 3; i++) {
    console.log(Math.random());
    await sleep(1000);
  }
}

main()
```

async/awaitの方がだいぶ直感的ですね！！
「Promiseのみを使う」の``let myPromise = main(1000);``で最初にpending状態のPromiseを定義するのも違和感がありますね。

半日かけでじっくりやったので、Promiseおよびasync/awaitが分かった気になってます。
間違いがあればご指摘お願いします！！