【非同期処理】JavaScriptのPromise, async, awaitなどについての整理と使い方

# 非同期処理/同期処理について
C言語やPythonから入った私はコードは上から順に実行されるものだと思っていました。しかし、JavaScriptはクライアントサイドの言語であり、 **「ページ更新などレスポンスの即時性が求められているため非同期処理である」** とのことです。
##### ~同期処理とは~ #####
コードを上から順に処理していき、「サーバとのデータのやりとりなど時間がかかる作業を**待って**次の処理を行う」といったイメージです。
##### ~非同期処理とは~ #####
原則的にコードは上から順に処理していきますが、「サーバとのデータのやりとりなど時間がかかる作業と**並行して**次の処理を行う」

※詳細なイメージは@don-bu-rakkoさんの以下をご参考にして頂けると良いかと思います。
https://qiita.com/don-bu-rakko/items/b283829c4572a6425a5c
→[記事はこちらに移転したそうです](https://proglearn.com/2020/11/03/%e3%80%90js%e3%80%91%e5%90%8c%e6%9c%9f%e5%87%a6%e7%90%86%e3%81%a8%e9%9d%9e%e5%90%8c%e6%9c%9f%e5%87%a6%e7%90%86%e3%81%ae%e9%81%95%e3%81%84%e3%81%a8%e3%81%af%ef%bc%9f%e4%be%8b%e3%81%88%e8%a9%b1%e3%82%92/)
# Promiseとasync/awaitを平たく言えば
`Promise`は（オブジェクトとして）処理の状態を持たせて、処理の状態を変化させることで、引数で取る関数の処理を **非同期に進めたり**、 **待たせたり**することができます。
`async`で宣言した関数は`await`（演算子）を使うことができます。`await`を挟むと、`Promise`に変換することができ、処理終了後`resolved`の状態である`Promise`となります。`resolved`になるまで関数内の他の処理が一時停止するので、 **待つ**ことができます。

# Promiseについて
上記のような非同期処理であるJavaScriptにおいて、`Promise`（正確には`Promiseオブジェクト`オブジェクトなんですね！）は処理の **「状態」** を表すものです。処理順序の決まり（=Promise）を指定するものって感じですね。
**「状態」** は以下の3種類あります。（**太字**の英語は今後も出てくるかもしれないです）

:::note
**`Promise`** の状態
* 待機 (**pending**): 初期状態。成功も失敗もしていません。
* 成功 (**resolved**): 処理が成功して完了したことを意味します。
* 失敗 (**rejected**): 処理が失敗したことを意味します。
└細かくは、 **fulfilled**や **settled**もありますが、一旦上記3点でことは足ります。
:::
# Promiseの使い方
#### 基本的なPromiseの記述
Promiseは引数に即時関数をとります。中の関数の引数には`resoleve`**関数**と`reject`**関数**をとります。
```javascript
// 基本的な記述
const promise = new Promise(function(resolve,reject){
  //ここに処理を書きます
});
```
```javascript
// アロー関数での記述
const promise = new Promise((resolve, reject) => {
  //ここに処理を書きます
});
```
まず、この時点での **「状態(=`PromiseStatus`)」** は **`pending`** です。（初期状態）
この`PromiseStatus`を変化させるために使うものが`resolve()`と`reject()`です。
前者を呼び出すと`PromiseStatus`は **`resolved`** となり、後者を呼び出すと **`rejected`** になります。
そして`PromiseStatus`が変化すると、`resolved`だと`.then`が呼び出され、`rejected`だと`.catch`が呼び出されます。
`resolve()`と`reject()`は引数を取ると、それぞれ`.then`と`.catch`の引数に使われます。
```javascript
const promise = new Promise((resolve, reject) => {
  resolve(variable1);
  // reject(variable2);
})
  .then((variable1) => {
    console.log("resolveしました"); // こっちが実行される
  })
  .catch((variable2) => {
    console.log("rejectしました");
  });
```
```javascript
const promise = new Promise((resolve, reject) => {
  // resolve(variable1);
  reject(variable2);
})
  .then((variable1) => {
    console.log("resolveしました");
  })
  .catch((variable2) => {
    console.log("rejectしました"); // こっちが実行される（エラー実行のイメージです）
  });
```
#### `.then`と`.catch`の連鎖
`resolve`したか、`reject`されたかで`.then`と`.catch`を連鎖させていくことができます。
変数の受け渡しは`return`を使用することで利用可能です。

なお、`.catch`が完了すると`PromiseStatus`は`resolved`になります。
（エラー後の処理を実行したことで、「エラーを解決した！」という理解）
そのため、`.catch`の後に`.then`を記述することで`.catch`後も連鎖させていくことができます。
```javascript
doSomething()
.then(function(result) {
  return doSomethingElse(result);
})
.then(function(newResult) {
  return doThirdThing(newResult);
})
.then(function(finalResult) {
  console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);
```
※コードは以下より引用
https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Using_promises

#### Promiseの同時並行処理
複数のPromiseを実行する際に、以下のような処理を行いたいときに使用するメソッドがあります。

:::note
**`Promise`** のメソッド
* **全て**のPromiseが終了したら実行 `Promise.all`
* **いずれか**のPromiseが終了したら実行 `Promise.race`
:::

```javascript
Promise.all([promise1, promise2]).then(() => {
  console.log("promise1とpromise2が終わりました");
});
```
```javascript
Promise.race([promise1, promise2]).then(() => {
  console.log("promise1かpromise2が終わりました");
});
```

# async/awaitとその使い方
`async`は非同期関数を定義する関数宣言で返り値は`Promise`オブジェクトとなります。
`async`で宣言した関数を実行した際に返ってくる`Promise`は`resolved`となっているため、`.then`で続けることができます。
`async`で宣言した関数内で`await`演算子を使用するとその処理が終了するまで関数内の実行を一時停止することができます。
`await`の配置次第で前述の`Promise.all`や`Promise.race`を実現することもできます。

```javascript
const asyncFunc = async () => {
  let x, y;
  // Promiseがresolveするまで待機
  x = await new Promise((resolve) => { // 2行下で返された1がawait演算子によってresolvedのPromiseとしてxに代入される
    setTimeout(() => {
      resolve(1); // 1000ms後にPromiseをresolvedにし、resolveの引数内の1を返す
    }, 1000);
  });
  // Promiseがresolveするまで待機
  y = await new Promise((resolve) => {
    setTimeout(() => {
      resolve(2);
    }, 1000);
  });

  console.log(x + y);
};
```
なお、`await`に続く式が`Promise`オブジェクトでない場合、`resolved`となった`Promise`に変換されます。
```javascript
async function f1() {
  var y = await 20;
  console.log(y); // 20
}

f1();
```
※コードは以下の記事より引用しました。
https://qiita.com/cheez921/items/41b744e4e002b966391a#await%E3%81%A8%E3%81%AF
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/await

# `Promise`オブジェクトになってしまったときの対応法
ところで、`await`を使うと、上記のように`Promise`で返ってくることがあります。`Promise`オブジェクトの中身を取ってくる方法が以下です。
ここでは`Promise`になってしまった`tmp`の値を取ってみます。
```javascript
// Promiseオブジェクトになってしまったtmp
tmp.then( function(value) {
  console.log(value) //valueがtmpの中身の値です
})
```


## 参考文献

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

https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Using_promises

https://qiita.com/uhyo/items/a9f6e70f43287cc0f52e

https://qiita.com/cheez921/items/41b744e4e002b966391a

https://qiita.com/soarflat/items/1a9613e023200bbebcb3

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/await

https://inspirationlife.jp/fetch_promiseobject/