Promiseを理解する(非同期処理, コールバックも)


# 非同期処理とは
**ある処理が実行されてから終わるまで待たずに、次に控えている別の処理を行うこと。**
そうすることで、時間のかかる処理を並行で処理し、その処理の完了を待たずに次の処理ができる。

*JavaScriptはシングルスレッドなため、通常では並列で複数の処理ができない。そのため効率的に処理できるように考えられた仕組み。*

# コールバック関数とは
別の関数(高階関数)に渡すための関数。

```javascript
// 例: setTimeout(callback, ms)
setTimeout(function() {
  console.log(5);
}, 1000);

setTimeout(() => { // アロー関数ver.
  console.log(5);
}, 1000);
```
```javascript:NG
// 1秒毎に数値を表示してカウントダウンしたいが、このコードでは1秒後にすべて実行される
setTimeout(() => console.log(5), 1000);
setTimeout(() => console.log(4), 1000);
setTimeout(() => console.log(3), 1000);
setTimeout(() => console.log(2), 1000);
setTimeout(() => console.log(1), 1000);
setTimeout(() => console.log(0), 1000);
```
```javascript:コールバック地獄
setTimeout(() => {
    console.log(5);
    setTimeout(() => {
        console.log(4);
        setTimeout(() => {
            console.log(3);
            setTimeout(() => {
                console.log(2);
                setTimeout(() => {
                    console.log(1);
                    setTimeout(() => {
                        console.log(0);
                    }, 1000);
                }, 1000);
            }, 1000);
        }, 1000);
    }, 1000);
}, 1000);
```

# Promiseとは
JavaScriptにおいて、**非同期処理の操作が完了したときに結果を返すもの。**
(後で返す という「約束」)
コールバック地獄を避けるために考えられた仕組み。

## ステータス
Promiseはステータス（状態）を持つ。

- **pending** （保留中）
- **fulfilled** （達成された）
- **rejected** （却下された）

## resolve, reject
Promiseの引数に設定するコールバック関数。

- 処理が問題なく完了すればresolveが実行される。 (ステータスはfulfilled)
- 問題があればrejectが実行される。 (ステータスはrejected)

## then(), catch()メソッド
次に処理したいことを登録しておく。

- then() : resolve()された場合の値を取得
- catch() : reject()された場合の値を取得

```javascript
getImage(file) // 画像ファイルの読み込みをする
  .then(image => compressImage(image)) // 画像を読み込んだら、圧縮する
  .then(cImage => saveImage(cImage)) // 圧縮が完了したら、その画像をDBに保存する
  .then(result => console.log(result)) // 保存した結果を出力する
  .catch(err => {throw new Error(err)})
```
このプログラムのどこでエラーが発生したとしても、エラーをキャッチする。
then()はfulfilled以外の場合は全てスルーして次の処理に渡すため。
エラーの場合、ステータスはrejectedになっているので、全てのthen()はこれをスルーする。最後に残ったcatch()だけがこれを掴むことができる。

# Async と Await
## コールスタックとキュー
同期処理、非同期処理ともに、まずはコールスタックに入る。
ただし、非同期処理のコールバックは、キューに入る。
コールスタックの処理が完了した後、キューに処理が残っている場合はそれをコールスタックに移し、処理が行われる。
**そのため、非同期処理は同期処理より遅く処理される。= コードの上から順に処理されない。**

## Async
**asyncが記述された関数は同期関数から非同期関数に変化し、常にPromiseを返す。**

コード中にPromiseを返さないreturnがある場合、JavaScriptは自動的にその値を解決（resolve）されたPromiseにラップする。
つまり、次の全てのコードは同じ振る舞いをする。

```javascript:1
async function func() {
    return 1;
}
```
```javascript:2
function func() {
    return Promise.resolve(1);
}
```
```javascript:3
function func() {
    return new Promise((resolve, reject) => {
        resolve(1);
    });
}
```
```javascript:実行
func().then((num) => {
    console.log(num); // 1~3ともに、結果は1
});
```

## Await
**asyncの中では、awaitを指定した行はPromiseが解決されるのを待つ。= 上から順に処理される。**(asyncは必須)
*つまり、非同期処理を同期処理っぽく動かせる。*

## Promise.all()
並行で(非同期で)処理するほうが効率がよい場面では、awaitで待たせてはいけない。
**Promise.all()を使うことで、複数の非同期処理を並列で処理し、全て完了させた後にコールバックできる。**(Promise.all()を待たせる(awaitする))

```javascript:bad
async function showNewData() {
  const allData = await fetchAllData();
  const oldData = await fetchOldData(); // allDataに待たされる
  showData(allData, oldData);
}
```
```javascript:good
async function showNewData() {
  const [allData, oldData] = await Promise.all([fetchData(), fetchOldData()]);
  showData(allData, oldData); // allData, oldDataともに完了すると実行される
}
```