マンガでわかる非同期処理 メモ

# リファレンスサイト

https://www.amazon.co.jp/%E3%83%9E%E3%83%B3%E3%82%AC%E3%81%A7%E3%82%8F%E3%81%8B%E3%82%8BJavaScript%E3%81%AEPromise-%E3%83%9E%E3%83%B3%E3%82%AC%E3%81%A7%E3%82%8F%E3%81%8B%E3%82%8B%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-%E6%9F%B3%E4%BA%95-%E6%94%BF%E5%92%8C-ebook/dp/B09XQ89M9Y

# 同期処理と非同期処理
- 同期処理
実行した順番に処理が実行される。
- 非同期処理
実行した順番に処理が実行されない。
```js:同期処理 
function func(i = null){
    console.log('同期処理'+ ` ${i}` + 'です。');
}
func(1);
func(2); //上の結果をまってから実行される
func(3); //上の結果をまってから実行される
```
```:結果
同期処理 1です。
同期処理 2です。
同期処理 3です。
```
```js:非同期処理
const asyncFunc = (time) => {
    setTimeout(() => {
        console.log(`${time}秒です。`);
    }, time * 1000);
};
func(1);
asyncFunc(3);
func(2);
asyncFunc(1);
func(3); 
```
```:結果 同期処理の後、非同期処理が実行される
同期処理 1です。
同期処理 2です。//asyncFunc(3);の実行結果を待たず
同期処理 3です。//asyncFunc(3);asyncFunc(1);の実行結果を待たず
1秒です。//asyncFunc(3);の実行結果を待たずにasyncFunc(1);がCB実行されている。
3秒です。
```
# コールバック関数
```js:高階関数とコールバック関数
function 高階関数(コールバック関数){
   コールバック関数()
}
```
- コールバック関数の必要条件
 関数の引数であること
 実行できる値であること
- 特徴
```js:コールバック関数は高階関数の中で引数が渡されて実行される。
// 高階関数はコールバック関数を引数にもつ関数
function 高階関数(コールバック関数){
   const 引数 = '引数';
   コールバック関数(引数);  //どのような引数が渡ってくるかは高階関数が決定する。
}

// 高階関数を実行する時
高階関数(console.log);//console.logの引数は高階関数が決定する
高階関数(alert);
高階関数((x)=>{ //無名関数 xは高階関数が決定する
   console.log(x);
});
```
- イベントリスナーや非同期処理にはコールバック関数が利用されている。
```js:
// イベントリスナーのコールバック関数はイベントハンドラーと呼ばれている。
// イベント実行したらどのような処理をするか記述する。

const button.onclick = () => { } // これも広義の意味でのコールバック関数になる。
button.addEventLisner('click',()=>{ });

// 非同期処理も 非同期処理完了後の処理をコールバック関数で記述する。
setTimeout(コールバック関数,1000); // １秒待機するという非同期処理後にコールバック関数が実行される。
```
- 非同期処理におけるコールバック関数のネストの問題
 非同期処理を同期させたい場合に発生する。
```js:引数のcbは非同期処理実行後に実行したい関数
setTimeout(() => {
    console.log(`3秒です。`);
}, 3 * 1000);

setTimeout(() => {
    console.log(`1秒です。`);
}, 1 * 1000);
```
```:非同期で実行される。
1秒です。
3秒です。
```
同期させたい場合、非同期処理のコールバック関数のなかで実行させるしかない。
```js:非同期処理のコールバック関数のなかで同期させたい処理を実行する。
setTimeout(() => {
    console.log(`3秒です。`);
//ネストして実行
    setTimeout(() => {
        console.log(`1秒です。`);
    }, 1 * 1000);
}, 3 * 1000);

```
```:同期された。
3秒です。
1秒です。
```
:::note info
上のネストを解消する仕組みが Promiseの thenメソッド になる。
:::

# Promise
- promise の特徴
インスタン時に同期的にPromiseのコールバック関数が実行される。
thenメソッドやcatchメソッドを発火させる条件は resolve,reject関数を実行させる必要がある。
- thenメソッド
Promiseオブジェクトのメソッド
返り値は新しいpromiseオブジェクト返すが次のthenメソッドを発火させる条件はthenメソッドのcbが実行された後になる。
thenメソッドのコールバック関数は非同期処理で実行される。
```js:thenメソッドのコールバック関数は非同期処理で実行される。
// cbは実行できる値 
const cb1 = function(){console.log('成功')};
const cb2 = ()=>{console.log('不成功')};

new Promise((resolve, reject) => {
     console.log('(^^)/ 同期 1');
     resolve(); //thenメソッドの第1引数を発火させる。
    // reject(); //thenメソッドの第2引数を発火させる。
})
// Promiseオブジェクトのメソッド
// cb1は非同期処理で発火 次のthenメソッドの発火条件はcbが完了された後になる。
.then(cb1,cb2)
// thenメソッドの返り値はpromiseオブジェクト返すのでチェーンできる。
.then(cb1,cb2);

console.log('(^^)/ 同期 2');
```
```:結果
(^^)/ 同期 1
(^^)/ 同期 2
成功
成功
```
```js:resolveとrejectでthenメソッドの引数が発火する。
new Promise((resolve, reject) => {
    // resolve();
    // reject();
}).then(cb1,cb2) // 実行されない。
.then(cb1,cb2); //上のthenメソッドのcbが実行されない限り実行されない。
```
注意点
- thenメソッドに実行できない値を渡してもエラーにならない。
then メソッドの引数には 必ず コールバック関数(実行可能な値) を渡す。
```js: 引数は実行できる値(コールバック関数)を渡す。
// cbは関数を返す。
const cb1 = function(){console.log('成功')};
const cb2 = ()=>console.log('不成功');

new Promise((resolve, reject) => {
     console.log('(^^)/ 同期 1');
// resolve と reject を実行しなくても
    // resolve();
    // reject();
// thenメソッドに 実行でききない引数をわたしてもエラーにならない。
}).then(cb1(),cb2()); // 第1,2 引数どちらとも同期処理で発火する。
console.log('(^^)/ 同期 2');
```
```:結果
(^^)/ 同期 1
成功
不成功
(^^)/ 同期 2
```
thenメソッドは非同期処理なので同期処理完了後、呼び出される。
```js:
const returnPromise = (value) => {
    return new Promise((resolve) => {
        console.log(value); //同期処理
        resolve();
    });
};

console.log("A"); //コールスタック1

returnPromise("B") //コールスタック2
    .then(() => {  //非同期処理はバックグラウンドで実行される。
    //バックグラウンドで非同期処理が完了するとそのコールバック関数がキューに追加される
        console.log("C");  
    })
    .then(() => { 
        console.log("D");
    });
returnPromise("E") //コールスタック3
    .then(() => { //非同期処理はバックグラウンドで実行される。
    //バックグラウンドで非同期処理が完了するとそのコールバック関数がキューに追加される
        console.log("F"); 
    })
    .then(() => { 
        console.log("G");
    });

console.log("H");//コールスタック4
```
```bash:結果
A  # 同期処理
B  # 同期処理
E  # 同期処理
H  # 同期処理
 # 非同期処理は同期処理が完了してから実行される。
C  # 非同期処理のコールバック関数
F  # 非同期処理のコールバック関数
D  # 非同期処理のコールバック関数後の非同期処理のコールバック関数
G  # 非同期処理のコールバック関数後の非同期処理のコールバック関数
```
```
コールスタック       キュー
----------------|----------------
console.log("A")| 
console.log("B")|
                | バックグラウンドで実行された非同期処理の完了後,そのCBがキューに追加される
                | -> console.log("C") 
console.log("E")| 
                | -> console.log("F")
console.log("H")|
-----------------------------------
コールスタック       キュー
----------------|----------------
console.log("C")| 
                | -> console.log("D")
console.log("F")| 
                | -> console.log("G")
-----------------------------------
コールスタック      キュー
----------------|----------------
console.log("D")| 
console.log("G")| 
```
# 非同期処理のチェーン
- Promiseの resolveとreject は 非同期処理の コールバック関数の中で呼びだす。
- 順番
  1. 非同期処理
  1. 非同期処理のCBで resolve,reject を実行する。
  1. それを受けてthenメソッドの引数が発火する。

```js:
const asyncFunc = (time, r=null) => {
    setTimeout(() => {
        console.log(`${time}秒です。`);
// 非同期処理のcbで resolve or reject を呼び出す。
        r && r();
    }, time * 1000);
};

new Promise((resolve)=>{
        asyncFunc(3,resolve);
}).then(()=>{
    return new Promise((resolve)=>{
        asyncFunc(2,resolve);
    });
}).then(()=>{
    return new Promise((resolve)=>{
        asyncFunc(1,resolve);
    });
});
```
```:結果同期で実行される。
3秒です。
2秒です。
1秒です。
```
注意点
thenメソッドで返ってくるPromiseの完了条件がCB実行後になるため、
thenメソッドでは必ず新しいPromiseのインスタンスでthenメソッドの返り値を上書して、完了条件をresolve,reject実行後にする必要がある。
```js:thenメソッドでPromiseをリターンしない場合
new Promise() // --- １⃣
.then()       
.then();      // 上のthenメソッドが返すPromiseでチェーンできる。

new Promise() // --- １⃣
.then(()=>{ // 1番目のPromise の resolve を 受けて引数が発火する。
// new Promise をreturn しない場合
    new Promise(); //   // --- ２⃣
})
.then();  // 上のthenメソッドのCBが実行されると発火される。


new Promise()  // --- １⃣
.then(()=>{
    return new Promise();  // --- ２⃣
})
.then();//２番目のPromiseのメソッドになる。
// 👆 よって 2番目のPromise の resolve,reject を 受けて引数が発火する。
```
```js:thenメソッドでPromiseをリターンしない場合 実際の例
// 非同期関数
const asyncFunc = (time,r) =>{
    setTimeout(()=>{
        console.log(`${time}秒です。`)
// 非同期処理のcbで resolve,reject を呼び出す
        r();
    },time*1000);
}

new Promise((r)=>{ // -- １⃣
    console.log('(^^)/ 同期 1');
    asyncFunc(3,r);
})
.then(()=>{ // -- 1⃣
// Promise を リターンしない。
    new Promise((r) => {
        console.log('1のthenメソッド');
        asyncFunc(2, r);
    });
})
.then(()=>{ // 👆のthenメソッドのcb関数実行後、非同期処理の完了をまたずに発火する。
    new Promise((r) => {
        console.log('2のthenメソッド');
        asyncFunc(1, r);
    });
});
console.log('(^^)/ 同期 2');
```
```:結果
(^^)/ 同期 1
(^^)/ 同期 2
3秒です。
1のthenメソッド
2のthenメソッド
1秒です。
2秒です。
```
```js:新しいPromiseのインスタンスを返す場合
new Promise((r) => { // -- １⃣
    asyncFunc(3, r);
})
// １⃣のresolveをうけて 1⃣のthenメソッド は非同期処理で発火
// 今度は １⃣のthenメソッドの resolve を待って ２⃣のthenメソッドが発火する。
.then(() => { // -- 1⃣
   //  new Promise((r) => {
   return new Promise((r) => { // -- ２⃣
        console.log('1のthenメソッド');
        asyncFunc(2, r);
    });
})
.then(() => { // -- 2⃣ // ２⃣のプロミスのthenメソッドになるため
                       // ２⃣のresolveをまって発火する。
    return new Promise((r) => {
        console.log('2のthenメソッド');
        asyncFunc(1, r);
    });
});
```
```:結果
3秒です。
1のthenメソッド
2秒です。
2のthenメソッド
1秒です。
```
:::note info
1. thenメソッドはPromiseを返すのでthenメソッドでチェーンできる。
1. Promiseのresolve,rejectは、非同期処理のコールバック関数で呼び出す。
   要するに非同期処理のコールバック関数で記述していた次の非同期処理をresolve,rejectで置き換えただけ。
:::

# resolve,reject の引数
- ローカルスコープ２⃣からローカルスコープ１⃣の変数にアクセスしたい場合
resolveとrejectの引数に変数を渡すことでthenメソッドの引数に値を渡せる。
- 引数のバケツリレー
  ローカルスコープで囲まれているため、渡したい変数や値は引数でバケツリレーする必要がある。
```js:スコープについて
// グローバルスコープ

new Promise((resolve,reject)=>{
    // ローカルスコープ --１⃣
    resolve(実引数1);
// アクセスできるスコープは
    // グローバルスコープとローカルスコープ１⃣
})
.then((仮引数2)=>{ // この仮引数2 に 実引数1 が渡ってくる。
    // ローカルスコープ --２⃣

// アクセスできるスコープは
    // グローバルスコープと仮引数2とローカルスコープ２⃣
   return 実引数2;
})
.then((仮引数3)=>{ // この仮引数3 に thenメソッドでreturnした値が渡る。
    // ローカルスコープ --３⃣

// アクセスできるスコープは
    // グローバルスコープと仮引数3とローカルスコープ３⃣
});
```
# reject関数について
1 thenメソッドの第２引数を発火させる。
```js
new Promise(function(resolve,reject){
    setTimeout(function(){
        reject();
    }, 1000);
})
.then(cb1,cb2); //cb2が発火する
```
2 第２引数がない場合は、catchメソッドを発火させる。
```js
new Promise(function(resolve,reject){
    setTimeout(function(){
        reject();
    }, 1000);
})
.then(cb1)
.then(cb1)
.catch(cb1);//catchメソッドが発火する
```
3 両方混在している場合
```js
new Promise(function(resolve,reject){
    setTimeout(function(){
        reject();
    }, 1000);
})
.then(cb1,cb2) //cb2が発火
.then(cb1,cb2) //cb1が発火
.catch(cb1);   //直のthenメソッドで例外が発生した場合発火
```
# async と await
Promiseの引数のバケツリレーを回避した非同期処理を同期させるための
Promiseにかわる新しい書き方
```js:
// 返り値がCBによるブロックスコープを形成しないため
// 他のPromiseからもアクセスできるようになる。
(async () => {
// awaitの戻り委はPromiseを返す必要がある
    const 引数1 = await new Promise((r) => {
        asyncFunc(3, r.bind(null, '引数1'));
    });
    const 引数2 = await new Promise((r) => {
        asyncFunc(2, r);
        console.log(引数1);
    });
    const 引数3 = await new Promise((r) => {
        asyncFunc(1, r);
        console.log(引数1);
    });
})();
```
# async await の reject 処理
try{}catch(e){}で処理する。
```js:
(async ()=>{
    try{
        const 引数1 = await new Promise((r,j)=>{
            j('エラーです。');
        });
    }catch(e){
        console.log(e);
    }
})();
```
## Promise でのエラーハンドリング

https://ja.javascript.info/promise-error-handling

```js:promiseには暗黙のtry catchがある。
        new Promise(function (resolve, reject) {
            
            resolve("ok");
//            reject(new Error("Whoops!"));

        }).then(function (result) {

//            throw new Error("Whoops!");
            blabla(); // error

        }).catch(alert); // エラーの取得  
```
非同期処理の中でエラーが生じたときのエラーハンドリング
```js:注意事項-これはcatchしない。
new Promise(function(resolve, reject) {
//         noSuchVariable  //ここで死んだ場合はcatchしてくれる。
  setTimeout(() => {
// 非同期処理のCBでエラー
        throw new Error("Whoops!");
   }, 1000);
}).catch(e=>alert(e)); //非同期処理のエラーは取得できない
```
```js:これもpromiseのcatchではcatchしない。
new Promise(function(resolve, reject) {
  setTimeout(() => {
     try{
        noSuchVariable; // スクリプトはここで死にます
     }catch{ //ここではチャッチしてくる。
        throw new Error("Whoops!");
     }
   }, 1000);
}).catch(e=>alert(e)); //ここではチャッチしてくれない。
```
```js:こうする必要がある。
    new Promise(function (resolve, reject) {
        setTimeout(() => {
            try {
                noSuchVariable; // スクリプトはここで死にます
            } catch {
//              throw new Error("Whoops!");
                reject("Whoops!");
            }
        }, 1000);
    }).catch(e => alert(e)); //ここでチャッチしてくる。
```
## 発展 イテレーターと非同期処理
```js:
const arr = [700, 600, 500];
(async () => {

// const res = await arr.map( // mapの戻り値は配列 
                              // promise ではないので await は無意味
})();

//cbをasyncにしても、map 関数の各イテレーションでブロックされるわけではない。
    const res = arr.map(async (time) => {
        const res = await new Promise((r) => {
            setTimeout(() => {
                // console.log(`exec: ${time}`);
                r(`resolve ${time}`);
            }, time);
        });
        console.log(res);
        return res;
    });

    console.log(res);

```
```:結果
[ Promise { <pending> }, Promise { <pending> }, Promise { <pending> } ]
resolve 500
resolve 600
resolve 700
```
```js:map 関数の各イテレーションでブロックされるわけではない。
const arr = [700, 600, 500];

// asyncの戻り値もPROMISE
const asyncCb = async (time) => {
    const res = await new Promise((r) => {
        setTimeout(() => {
            r(`resolve ${time}`);
        }, time);
    });
    console.log(res);
    return res;
};
const myMap = (arr,cb)=>{
    const res = [];
    for (let i = 0; i < arr.length; i++) {
        const time = arr[i];
// ここでawaitされているわけではない。
        res.push(cb(time));
    }
    return res;
};
const res = myMap(arr,asyncCb);
console.log(res);
```
```:結果
[ Promise { <pending> }, Promise { <pending> }, Promise { <pending> } ]
resolve 500
resolve 600
resolve 700
```
```js:イテレーションを実行時に await を使用
const arr = [700, 600, 500];

const asyncCb = async (time) => {
    const res = await new Promise((r) => {
        setTimeout(() => {
            r(`resolve ${time}`);
        }, time);
    });
    console.log(res);
    return res;
};

const myMap = async (arr,cb)=>{
    const res = [];
    for (let i = 0; i < arr.length; i++) {
        const time = arr[i];
// ここでawait
        res.push(await cb(time));
    }
    return res;
};

(async ()=>{
    const res = await myMap(arr,asyncCb);
    console.log(res);
})()
```
```:結果
resolve 700
resolve 600
resolve 500
[ 'resolve 700', 'resolve 600', 'resolve 500' ]
```
各イテレーションごとに待機するため、処理が非常に遅くなる。

# Promise の 静的メソッド
promiseオブジェクトの配列を処理する。
各Promiseは並列で処理される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/8f1223bd-e3cf-6490-0356-4205e79f3f0c.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/e85efb24-ffd9-6315-6ef0-9ece9c458e53.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/3894bd91-fd11-a864-b9db-2391e1c849eb.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/c8268bdc-005a-6907-67ec-a1e47dd62957.png)
- allメソッド
 全てのPromiseが解決したらthenメソッドの第1引数が発火する。
 戻り値は 各Promiseの結果の配列 になる。
 どれか一つでもrejectならrejectのタイミングで第2引数が発火する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/f1a33a06-f900-a22f-f4bb-e2097d46d159.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/452fc9b9-818a-457a-c452-7ca856410763.png)
  - async の時
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/97c40770-2445-e2b2-ecb7-812ecb43b9bd.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/4e1c7573-1360-59b2-e6e7-ec43aeee6013.png)

```js:並列に処理するため処理が早くなる。
const arr = [700, 600, 500];

(async () => {
    const res = await Promise.all(arr.map((time) => {
        return new Promise((r) => {
            setTimeout(() => {
                console.log(`exec: ${time}`);
                r(`resolve ${time}`);
            }, time);
        });
    }));
    console.log(res); 
})();
```
```:結果 並列処理されている
exec: 500
exec: 600
exec: 700
[ 'resolve 700', 'resolve 600', 'resolve 500' ]
```

- allSettledメソッド
  全ての配列の要素の結果が配列として変える。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/ac4d2f24-5701-91fb-029a-9876209a69f1.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/3b23ae88-d209-6b74-2779-fa59c7389221.png)

- anyメソッド
最初に履行されたプロミスの値を返す
すべてのプロミスが拒否された場合のみエラーでrejectを返す
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/05a24c9d-c396-1590-8114-f500c05da615.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/125b1146-191d-c360-f476-a2425fc218ec.png)

- raceメソッド
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/ef58a39d-ac85-41cb-3953-e295a98d865a.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/ac2b4511-bfaf-417b-4f31-c146bf05e570.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/08079e1c-2f5a-3e36-f1db-8d0faa198556.png)

# 静的メソッド2 resolve() or reject()
1. promise の テストに使用する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1830335/63f3d4e6-470e-5de2-276c-fe89b97f436c.png)

2. thenメソッドをもったオブジェクト
```js:Promise.resolve
const thennable = {
    then(r,j) {
        setTimeout(() => {
            console.log('(^^)/');
        }, 3000);
    }
};
// thenableのthenメソッドを実行する。
Promise.resolve(thennable);

// thenableのthenメソッドは実行されない。
Promise.reject(thennable)
```
```js:Promise.resolveのthenメソッド
const thennable = {
    then(r,j) {
        setTimeout(() => {
            console.log('(^^)/');
// r を実行することで 次のthenメソッドの第1引数が発火する。
            r();
        }, 3000);
    }
};
Promise.resolve(thennable)
// thenableの中で rやj が実行されている必要がある。
.then(
    () => {
        console.log('resolve');
    },
    () => {
        console.log('reject');
    }
);
```
```:結果
 //3秒後
(^^)/
resolve
```
```js:
// thenableを rejectメソッドで利用することはない。
Promise.reject(thennable)
.then(
    () => {
        console.log('resolve');
    },
    () => {
        console.log('reject');
    }
);
```
```:結果
reject
```