【JavaScript】非同期処理の順番を指定する「Promise」の基本を理解するためのサンプルコード

## はじめに
JavaScript(ES6)で**非同期処理を順番に実行**するのに便利な**Promise**の基本についてまとめました。

```
1. Promiseオブジェクトの挙動
2. 通常のコールバック関数
3. 上記コールバック関数をPromiseオブジェクトで書き換え
4. Promiseチェーンで非同期処理の順番を指定
```

の4つに分けて記載してあります。


## 環境
```yaml
OS: macOS Catalina 10.15.1
```

## Promiseのメリット
[公式ドキュメント](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Using_promises)より

> 現在の JavaScript イベントループの実行完了より前には、コールバックが決して呼び出されない。

> 非同期処理が完了もしくは失敗した後に then() により登録されたコールバックでも、上記のように呼び出される。

> then() を何回も呼び出して複数のコールバックを追加してもよく、それぞれのコールバックは追加順に独立して実行される。

つまり、**非同期処理の順番**がコントロール出来るというのが最大のメリットですね。

本記事では、そんな**Promise**の

- 基本的な挙動、
- 従来のコールバック関数との違い
- Promiseチェーン

を確認していきます。

## 1. Promiseオブジェクトの挙動
まずは、**Promiseオブジェクト**とはどのようなものか？
シンプルな例で確認してみます。

```javascript:promiseSample.js
const samplePromise = new Promise((resolve, reject) => { //Promiseはresolve, rejectの2つの引数を持つ
  let a = true //ここをtrue/falseで変更して挙動を確認(後述)
  if (a === true) {
    resolve('Success') //trueならresolveの()内が返される
  } else {
    reject('Failed') //falseならrejectの()内が返される
  }
})

//出力部
samplePromise.then((message) => { //thenの引数messageはresolveの()内に記述した内容
  console.log(`This is in the then ${message}`)
}).catch((message) => { //catchの引数messageはrejectの()内に記述した内容
  console.log(`This is in the catch ${message}`)
})
```
↓

```shell:出力例1：a=trueの場合
This is in the then Success
```

```shell:出力例2：a=falseの場合
This is in the catch Failed
```

```shell:出力例3：a=truの場合(タイポ。未定義の変数を代入)
This is in the catch ReferenceError: tru is not defined 
```

出力例3のように、**エラーが発生したら、それが引数となる**のにも注目です。

では、次にPromiseで置き換える**前の**コールバック関数をご確認下さい。

## 2. 通常のコールバック関数

```javascript:callbackFunction.js
const cat = false //猫が好きならtrueにして下さい
const dog = false //犬が好きならtrueにして下さい

//コールバックで書いた場合
function callbackSample(callback, errorCallback) { //callback関数, errorCallback関数を引数に持つ
  if (cat && dog) { //犬猫両方好きな場合
    callback('Both dogs and cats are cute :)') //文字列を持つcallbackを返す
  } else if (dog) { //犬だけ好きな場合
    errorCallback({
      name: 'Dog',
      message: 'Wan!!'
    })
  } else if (cat) { //猫だけ好きな場合
    errorCallback({
      name: 'Cat',
      message: 'nyaaaaa~'
    })
  } else { //...
    errorCallback({
      name: 'Oh...',
      message: 'You may not be friend. :('
    })
  }
}

//出力部
callbackSample((message) => { //第一引数はcallback関数の()内に渡した文字列
  console.log(`Success: ${message}`)
}, (error) => { //第二引数はerrorCallback関数の()内に渡した文字列
  console.log(`${error.name}  ${error.message}`)
})
```
↓

```shell:出力例1：cat,dog両方trueの場合
Success: Both dogs and cats are cute :)
```

```shell:出力例2：dogだけtrueの場合
Dog  Wan!!
```

```shell:出力例3：catだけtrueの場合
Cat  nyaaaaa~
```

```shell:出力例4：cat,dog両方falseの場合
Oh...  You may not be friend. :( 
```

これを**Promiseオブジェクト**を使って書き換えます。

## 3. Promiseオブジェクトを使って置き換えた場合

```javascript:promiseFunction.js
const cat = false //猫が好きならtrueにして下さい
const dog = false //犬が好きならtrueにして下さい

// Promiseで変更後
function promiseSample() { //引数に渡していたcallback, errorCallback関数が不要になる
  return new Promise((resolve, reject) => { //Promiseオブジェクトを定義し、その中に全ての処理を書く
    if (cat && dog) {
      resolve('Both dogs and cats are cute :)') //callback => resolveに置き換える
    } else if (dog) {
      reject({ //errorCallback => rejectに置き換える
        name: 'Dog',
        message: 'Wan!!'
      })
    } else if (cat) {
      reject({ //errorCallback => rejectに置き換える
        name: 'Cat',
        message: 'nyaaaaa~'
      })
    } else {
      reject({ //errorCallback => rejectに置き換える
        name: 'Oh...',
        message: 'You may not be friend. :('
      })
    }
  })
}

//出力部
promiseSample() //then, catchをメソッドチェーンで繋げる
  .then((message) => { //thenでresolveの()内を引数messageで受け取る
    console.log(`Success: ${message}`)
  })
  .catch((error) => { //catchでrejectの()内を引数errorを受け取る
    console.log(`${error.name}  ${error.message}`)
  })
```
↓

```shell:出力例1：cat,dog両方trueの場合
Success: Both dogs and cats are cute :)
```

```shell:出力例2：dogだけtrueの場合
Dog  Wan!!
```

```shell:出力例3：catだけtrueの場合
Cat  nyaaaaa~
```

```shell:出力例4：cat,dog両方falseの場合
Oh...  You may not be friend. :( 
```

同様の結果が得られました。

では、次が最後です。**非同期処理の順番**を指定してみましょう！

## 4. Promiseチェーンで非同期処理の順番を指定する
以下コードを読む前に、**子供が目を閉じて数を数えているシーン**を思い浮かべて下さい...きっと理解が進みます。笑

※順番の指定に対して理解しやすくするため、先程までとは違い`reject`は使っていません。

```javascript:methodChain.js
function promiseOne() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('いーち')
    }, 1000) //1秒後に'いーち'という文字列を返す
  })
}

function promiseTwo() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('にーい')
    }, 1000) //1秒後に'にーい'という文字列を返す
  })
}

function promiseThree() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('さーん')
    }, 1000) //1秒後に'さーん'という文字列を返す
  })
}

//出力例
promiseOne()
  .then(one => { //ここのoneにはpromiseOneのresolveの()内にある文字列が入る
  console.log(one)
  return promiseTwo() //ここでreturnした結果が次の引数twoに入る
})
  .then(two => { //ここのtwoにはpromiseTwoのresolveの()内にある文字列が入る
    console.log(two)
    return promiseThree() //ここでreturnした結果が次の引数threeに入る
})
  .then(three => { //ここのthreeにはpromiseThreeのresolveの()内にある文字列が入る
  console.log(three)
})
```
↓

```shell:出力(1秒後)
いーち
```
```shell:出力(2秒後)
いーち
にーい
```
```shell:出力(3秒後)
いーち
にーい
さーん
```

全て**1秒後**に文字列を返す関数でしたが、キレイに3秒かけて「いーち、にーい、さーん」と返してくれます。

これで、**非同期処理の順番**を指定することが出来ました！

以上です！

## おわりに
最後まで読んで頂きありがとうございました:bow_tone1:

どなたかの参考になれば幸いです:relaxed:

## 参考にさせて頂いたサイト（いつもありがとうございます）
- [JavaScript Promises In 10 Minutes - YouTube](https://www.youtube.com/watch?v=DHvZLI7Db8E&t=87s)