【超有料級】非同期処理がスッと理解できる魔法のレッスン。今日からあなたも非同期の魔法使い！

## はじめに
今回の記事では、多くの人が一度はつまずいたり、挫折した経験のある「非同期処理」について解説します。

「非同期処理」と聞いて、「うわ、無理…」「なんか苦手…」と感じる人も少なくないのではないでしょうか？
実は、僕自身もこの分野がめちゃくちゃ苦手でした。
プログラミングの処理は基本的に、上から下へ順番に実行されていきます。しかし、非同期処理では処理の途中で他の処理を実行し、非同期のタスクが完了したタイミングでその結果を使って画面を描画したり、データベースにアクセスしたりします。
たとえば画像表示サイトでは、ユーザー情報の取得を待たずに、先に画面操作ができるようになることがあります。
非同期処理は、まさにそういった仕組みを実現するために使われています。

でも正直に言うと、初めて非同期処理を学んだときは、本当に意味がわかりませんでした。
頭の中をミキサーでぐちゃぐちゃにされたような感覚で、「なんでこうなるの？」と何度も混乱しました。
それでも、何度もつまずきながら試行錯誤を繰り返し、ようやく非同期処理の考え方と仕組みが理解できるようになりました。

この記事では、そんな僕がついに「腹落ちした」非同期処理の本質について、できるだけわかりやすくまとめました。

また、この記事には簡単な練習問題も用意していますので、
読むにあたって、以下の2つの準備をおすすめします：
- 紙とペン、そして VS Code や Cursor などのエディターを用意しましょう
　実際にコードを書きながら、一つひとつの動きを確認して進めてみてください。
- 紙に図を書いてみましょう
　後ほど紹介する図を、自分の手で書いてみましょう。非同期処理のタスクがどこで管理され、どの順番で実行されるのかを可視化することで、理解度が一気に深まります。


この2つを実行すれば、記事を読み終えるころには、非同期処理への苦手意識がなくなっているはずです。
「騙されたと思って」ぜひ実践してみてください！

## この記事を読むことで得られること
実際に手を動かしてコードを書いたり、処理順番を図にしたりすることで理解度倍増！
JavaScriptの難関、非同期処理について処理を視覚的に確認することができ、
非同期処理への苦手意識がなくなります。

### 目次







- [同期処理について](#同期処理について)
- [非同期処理について](#非同期処理について)
  - [代表的な非同期処理](#代表的な非同期処理)
  - [コールスタックイベントループタスクキュー関係図](#コールスタックイベントループタスクキュー関係図)
- [非同期処理コールバック](#非同期処理コールバック)
- [コールバック地獄](#コールバック地獄)
- [async/awaitとPromiseの比較](#asyncawaitとpromiseの比較)
- [trycatchで例外処理](#trycatchで例外処理)
    - [カスタムエラー](#カスタムエラー)
- [マクロタスクとマイクロタスク](#マクロタスクとマイクロタスク)
- [非同期の並列処理](#非同期の並列処理)
  - [promise.all(反復可能オブジェクト)](#promiseall反復可能オブジェクト)
  - [promise.race(反復可能オブジェクト)](#promiserace反復可能オブジェクト)
  - [promise.allsettled(反復可能オブジェクト)](#promiseallsettled反復可能オブジェクト)









## 同期処理について

- メインスレッドでコードが順番に実行される処理
    - 同期処理では一つの処理が完了するまで次の処理は実行されない
    
    ```jsx
    function sleep(ms) {
      const startTime = new Date();
      while (new Date() - startTime < ms);
      console.log('sleep done');
    }
    
    const btn = document.querySelector('button');
    btn.addEventListener('click', function(){
      console.log('button clicked');
    });
    
    sleep(3000)
    ```
    
    - `sleep`関数は現在の時刻ー関数実行した時の時刻（`startTime`）を引数に取ったms(ミリ秒)間の差になるまでメインスレッドを占有する処理
    - `sleep`の実行が終わらない限り、次の処理（`console.log('button clicked');`）は行われない



## 非同期処理について

- メインスレッドから処理を一時的に切り離されて実行される
    - メインスレッドはコールスタックで管理され、メインスレッドから切り離された非同期処理はタスクキューで管理されている
    - 非同期処理ではメインスレッドから処理が切り離されているのでメインスレッドでの処理は継続される
- タスクキュー(マクロタスク・マイクロタスク)で処理する順番を管理している（先入れ先出し）
    - コールスタックとイベントループと連携している
        - イベントループは定期的にコールスタックにコンテキストが積まれていないかを確認する仕組み
        - **コールスタックに処理が積まれていない場合にタスクキューからコールスタックに処理を移行し実行する**
    
    ```jsx
    function sleep(ms) {
      const startTime = new Date();
      while (new Date() - startTime < ms);
      console.log('sleep done');
    }
    
    const btn = document.querySelector('button');
    btn.addEventListener('click', function(){
      console.log('button clicked');
    });
    
    setTimeout(function (){
    	sleep(3000)
    ,2000})
    ```
    
    - `setTimeout`は非同期処理でメインスレッドから切り離されている。関数実行（ページ読み込み）から2000（2秒間）経って` sleep(3000)`が実行される
    - メインスレッドから切り離されている時間(2秒間)はメインスレッドでの後続の処理が可能

### 代表的な非同期処理

非同期処理API

- setTimeout
- Promise
- queueMicrotask

etc…

UIイベント

- クリック

etc…

NWイベント

- HTTPリクエストの送受信
- ソケット通信（WebSocketなど）

…etc

I/Oイベント

- ファイル読み書き（`fs.readFile` など）
- データベースとの通信
- 標準入力や出力（ターミナルとのやりとり）

…etc
<br>


### コールスタック、イベントループ、タスクキュー関係図
いろいろと用語が出てきて、「結局どう関係してるの？」と混乱しているかもしれません。
コールスタック？イベントループ？タスクキュー？
言葉だけだと関係性のイメージがつかみにくいですよね。

そんなときは、ぜひ以下の図を紙に手で書いてみてください。
[![Image from Gyazo](https://i.gyazo.com/d5a12d8a4b62cc8281e488db97b46a2f.png)](https://gyazo.com/d5a12d8a4b62cc8281e488db97b46a2f)



最初は難しく感じるかもしれませんが、図に描くことで処理の流れが目に見えるようになり、理解が一気に深まります。

※この段階では「ジョブキュー」と「タスクキュー」の違いは気にしなくてOKです。
どちらもまとめて「タスクキュー」として考えて進めていきましょう。



## 非同期処理コールバック

```jsx
function a() {
  setTimeout(function task1() { 
    console.log('task1 done');
  });

  console.log('fn a done');
}

function b() {
  console.log('fn b done');
}

a();

b();

//コンソール出力
'fn a done'
'fn b done'
'task1 done'
```

#### 練習問題
先程ののコードでは、a()を実行したあとにb()を実行していますが、
実行結果としては b() が task1 より先に表示されてしまいます。
では、「task1が完了したあとに関数bを実行する」には、どのようにコードを書き換えれば良いでしょうか？
先ほど紹介した[関係図](#コールスタックイベントループタスクキュー関係図)に書き込むと視覚的に理解できますよ！
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>



回答
```jsx
function a() {
  setTimeout(function task1() { 
    console.log('task1 done');
    b();
  });

  console.log('fn a done');
}

function b() {
  console.log('fn b done');
}

a();

```

### コードの流れ

関数`a`を実行（コールスタックに関数`a`の関数コンテキストとグローバルコンテキストが積まれる）

→　タスクキューに関数を`task1`を追加
→　コールスタックから関数`a`の関数コンテキストが消滅　
→　コールスタックからグローバルコンテキストが消滅　
→　タスクキューから関数`task1`がコールスタックに移行　
→　関数`task1`が実行　
→　関数 `b`を実行（コールスタックに関数`b`の関数コンテキストが積まれる）　
→　関数`b`が実行
上記のように非同期とコールバックの仕組みを使うことで関数の順序を変えることができる

## コールバック地獄

```jsx
function sleep(callback, val) {
    setTimeout(function () {
        console.log(val++)
        callback(val)
    }, 1000)
}

sleep(function (val) {
    sleep(function (val) {
        sleep(function (val) {
            sleep(function (val) {

            }, val)
        }, val)
    }, val)
}, 0)
```

### コードの流れ

`sleep`

- `callback`と`val`を引数に取る
- `setTimeout` を使い1秒後、コンソールに`val`を表示して表示した後にプラス1される
- プラス1されて値が`callback`の引数に渡され処理される

`sleep`使用側

- `sleep` の1回目は、引数 `0` を受け取り、1秒後に `console.log(val++)` で `0` を表示。その後、`val` は `1` にインクリメントされ、次の `callback` に `1` が渡される。**この時点では、渡された `1` はまだコンソールには表示されない（次の `sleep` 内で表示される）。**
- `sleep` の2回目は、前のコールバックから受け取った `val = 1` を引数にして呼び出され、1秒後に `console.log(val++)` で `1` を表示。その後、`val` は `2` になり、次の `callback` に `2` が渡される。**この時点でも、`2` はまだ表示されない。**

以下、繰り返し

### 出力結果

| 時間経過 | val | `console.log(val++)` の出力 | 次に渡すval |
| --- | --- | --- | --- |
| 0秒 | - | - | sleep(0) 開始 |
| 1秒後 | 0 | `0` | 1 |
| 2秒後 | 1 | `1` | 2 |
| 3秒後 | 2 | `2` | 3 |
| 4秒後 | 3 | `3` | 4 |


このコードは正常に動作しますが、コールバック関数のネストが深くなり、コードの可読性や保守性が著しく低下してしまいます。
このようにコールバックを連ねて非同期処理をつなげる方法は「コールバック地獄」と呼ばれ、推奨されません。

そこで、JavaScriptのPromiseやasync/awaitを使うことで、よりシンプルで読みやすい非同期処理の記述が可能になります。
次のセクションでは、同じ処理をPromiseとasync/awaitで書き直してみましょう。







## Promise

- 非同期処理をより簡単に、可読性が上がるように書けるようにしたもの

コールバック地獄の解消

```jsx
function sleep(val) {
  return new Promise(function (resolve) {
    setTimeout(function () {
      console.log(val++);
      resolve(val);
    }, 1000);
  })
};

sleep(0)
.then(val => sleep(val))
.then(val => sleep(val))
.then(val => sleep(val));
```
### コードの流れ

 `sleep`関数 

- 引数に `val` を受け取る
- `new Promise` により Promise インスタンスを生成
    - `Promise` コンストラクタのコールバック関数は、`resolve` と `reject` という2つの引数を取る（今回は `resolve` のみ使用）
- `setTimeout` を使って1秒後に以下の処理を行う：
    - `console.log(val++)` で現在の `val` を表示し、その後 `val` をインクリメント
    - `resolve(val)` を呼び出し、次の `.then()` に値を渡す
- `Promise` インスタンスを返すことで、`.then()` で非同期処理をつなげることができる


`sleep`使用側

- `sleep(0)` を最初に呼び出し、`.then()` を使ってその後の処理をチェーンさせていく
- 各 `.then()` の中では次の `sleep(val)` を呼び出して return することで、処理の流れが順番に続いていく
- `.then()` に渡された関数は、前の `resolve(val)` の結果を引数として受け取る
- **`.then()` に処理をつなげるには、常に `Promise` を返す必要がある。**
    - `resolve(val)` → `.then(val => ...)`
    - `reject(err)` → `.catch(err => ...)` に値が渡される

`.then/.catch` で処理をつなげるために、**必ず戻り値に`Promise`のインスタンスを返す**

- `resolve` → `then`
- `reject` →　`catch`





## async/await

`Promise`をより直感的に書けるようにしたもの

```jsx
// async 関数を変数に代入するパターン
const fetchData = async (url) => {
  const response = await fetch(url);
  // 後続の処理を書く
};

// 通常の関数宣言パターン
async function fetchData(url) {
  const response = await fetch(url);
  // 後続の処理を書く
}
```

### コードの流れ
- `async`
    
    関数の前に `async` を付けることで、**その関数を非同期関数（Async Function）として定義**できる。
    
    - `async` 関数は常に **`Promise` を返す（戻り値を自動的に Promise にラップする）**
    - `Promise` が解決（`resolve`）されるまで **処理を待機できる**
    - 例：`return 1` と記述しても、実際は `Promise.resolve(1)` を返す
- `await`
    
    `await` は **Promise の完了を待つ演算子**。
    
    - `await` を使うと、Promise が解決されるまで **後続の処理を一時停止**する
    - **`async` 関数の中でのみ使用可能**
        - `async` でない関数内で使用すると **エラーになる**
    - 一般的に、`async` 関数と `await` は **セットで使われる**


## async/awaitとPromiseの比較

```jsx
function sleep(val) {
  return new Promise(function (resolve) {
    setTimeout(function () {
      console.log(val++);
      resolve(val);
    }, 1000);
  })
};

async function init (){
  let val = await sleep(0);
  val = await sleep(val)
  val = await sleep(val)
  val = await sleep(val)
  // この return は実行されません（上の throw により中断される）
  console.log(val)
} 

init();
```
同じ処理を Promise チェーンで書いた場合

```jsx
sleep(0)
.then(val => sleep(val))
.then(val => sleep(val))
.then(val => sleep(val));
```

- 上記の `async/await` の実装は、[Promise](https://www.notion.so/2093f08f2a8580b287b5ff9bc979b9c8?pvs=21) セクションで紹介した処理と **同じ挙動**
- `await` を使うと `resolve(val)` の引数 `val` が、そのまま `init` 関数内の `val` に代入される
    - 一方、Promise では `.then(val => ...)` を使って **明示的に受け渡す必要**
- 両者を比較すると、`async/await` を使う方が **よりシンプルで読みやすく、直感的**に記述できる
- **なお、`await` を付けずに呼び出すと、戻り値は Promise オブジェクトになる**

更に関数`init`（`async`関数）は **`Promise`を返しますので`then`や`catch`で処理をつなげられる**

```jsx
//・・・上略（sleep関数）

async function init (){
  let val = await sleep(0);
  val = await sleep(val)
  val = await sleep(val)
  val = await sleep(val)
  throw new Error ('not Data')
  return val
} 

init().then(function (data) {
  console.log(data);
}).catch(function(e) {
  console.error (e)
})
```

- 3回目の`await sleep(val)`の後、` throw new Error ('not Data')`が実行されエラーを出力される
- この場合`val`は返されず、エラーが発生して`catch`ブロックに処理が移る
- このように、`async` 関数内で発生した例外は **自動的に Promise の reject として扱われ**、`.catch()` で補足可能


## try&catchで例外処理

### コードの流れ

throwがある場合

```jsx
try {
    console.log("start")
    throw new Error ("No Data")
    console.log("task")
} catch (e) {
    console.error(e)
} finally {
    console.log("end")
}
```

- `try` ブロック内の `console.log("start")` がまず実行される
- 次に `throw new Error("No Data")` により例外が発生し、**その時点で `try` ブロックの処理は中断**される
    - よって、`console.log("task")` は実行されない
- 発生したエラーは `catch (e)` に渡され、変数 `e` に格納される
    - `e.message` には `"No Data"` が格納されており、`console.error(e)` によってエラーオブジェクトが出力される
- 最後に `finally` ブロックが実行され、`console.log("end")` によって `"end"` が出力される
    - `finally` は、**エラーの有無に関わらず必ず実行される**ブロック

throwがない場合

```jsx
try {
  console.log("start");
  console.log("task");
} catch (e) {
  console.error(e);
} finally {
  console.log("end");
}

```

- `try` ブロック内の処理はすべて正常に実行される
    - `console.log("start")` と `console.log("task")` の両方が出力される
- エラーが発生しないため、`catch` ブロックはスキップされる
- 最後に `finally` ブロックが実行され、`console.log("end")` が出力される


----

### try＆cachを使った例外処理

```jsx
async function fetchData() {
    const res = await fetch("users.json")
    if (res.ok) {
        const json = await res.json()
        if (json.length === 0) {
            throw new Error('No Data')
        }
        return json
    }
}

const userData = async function () {
    try {
        const users = await fetchData()
        for (const user of users) {
            console.log(`I'm ${user.name}(${user.age})`)
        }
    } catch (e) {
        console.error(e)
    } finally {
        console.log("end")
    }
}

userData();

```

### コードの流れ
fetchData（async関数）

- この関数では、`users.json` というファイルからデータを取得しているが、本来は API やデータベースから取得する想定
- 非同期で `users.json` ファイルからデータを取得する
- fetch によって返された Response オブジェクトの `ok` プロパティが `true` の場合、レスポンスを `.json()` メソッドで JavaScript の配列に変換する
    - 取得した配列が空（`json.length === 0`）だった場合
        - `throw new Error('No Data')` によってエラーを投げ、`catch` ブロックに処理が移る
    - 配列にデータがある場合
        - 配列データ（`json`）を返す
        

userData（async関数）

- `try` ブロック（正常にデータが取得できた場合）では
    - `fetchData()` の戻り値を `users` 変数に格納
    - `for...of` ループで `users` を順番に処理し、`console.log(`I'm ${user.name}(${user.age})`)` で各ユーザーの名前と年齢を出力
- `catch` ブロック（`throw new Error()` によってエラーが投げられた場合）では
    - エラーオブジェクト `e` を `console.error(e)` で表示
- `finally` ブロックでは
    - `try` または `catch` の処理が終了した後、必ず `console.log("end")` を実行して `"end"` を出力する


----


### カスタムエラー

try＆cachを使った例外処理のコードに下記を追加することでカスタムエラーの作成ができる

```jsx
class NoDataError extends Error {
    constructor(message) {
        super(message) 
        this.name = NoDataError

    }
}
```
### コードの流れ

- `NoDataError` というクラスを、組み込みの `Error` クラスを継承して定義している
- このクラスは、インスタンス化される際に `message` を引数として受け取る
- `super(message)` は親クラス（ここでは `Error` クラス）のコンストラクタを呼び出すもので、エラーメッセージの内容を `Error` オブジェクトとして正しく扱えるようにする
- `this.name = 'NoDataError'` によって、エラーオブジェクトの `name` プロパティが `"NoDataError"` に設定され、エラー出力時にカスタムエラー名が表示されるようになる

使用例

```jsx
throw new NoDataError('not Data')

const userData = async function () {
    try {
        const users = await fetchData()
        for (const user of users) {
            console.log(`I'm ${user.name}(${user.age})`)
        }
    } catch (e) {
	    if( e instanceof NoDataError) {
        console.error(e)
       } else {
		    console.error("error!!!")
       }
    } finally {
        console.log("end")
    }
}
```

- `Error`を継承した`NoDataError('not Data')`でエラーを投げて`catch`ブロックに処理を移行する
- `catch` ブロック内では、発生したエラーがどのクラス（インスタンス）から投げられたかを判定し、それに応じて処理を分岐している
- エラーが `NoDataError` のインスタンスであれば、そのエラーオブジェクトを `console.error(e)` で出力
- `NoDataError` 以外のエラー（たとえば `TypeError` や `ReferenceError` など）の場合は、`"error!!!"` という文字列を出力する

## マクロタスクとマイクロタスク

### マイクロタスク
マクロタスクよりも優先して実行される
    
例: `Promise`、`queueMicrotask` など
    
### マクロタスク
    
マイクロタスクがすべて完了してから実行される
例: `setTimeout`、`setInterval` など
    
- 処理の途中で新たにマイクロタスクが追加された場合でも、**マイクロタスクがすべて完了するまでマクロタスクは実行されない**


ここでもう一度先ほどの関係図を見るとマクロタスク（タスクキュー）、マイクロタスク（ジョブキュー）についてもイメージができると思います。
[![Image from Gyazo](https://i.gyazo.com/d5a12d8a4b62cc8281e488db97b46a2f.png)](https://gyazo.com/d5a12d8a4b62cc8281e488db97b46a2f)



```jsx
new Promise(function promise(resolve) {
  console.log('promise');

  setTimeout(function task1() {
    console.log('task1');
    resolve();
  });

}).then(function job1() {
  console.log('job1');

  setTimeout(function task1() {
    console.log('task2');
    queueMicrotask(function job4() {
      console.log('job4')
    })
  });

  
}).then(function job2() {
  console.log('job2');
}).then(function job3() {
  console.log('job3');
})

console.log('global end');

//コンソール出力
promise
global end
task1
job1
job2
job3
task2
job4
```

### コードの流れ（イベントループとタスクの順序）

ここで同期タスク終了 → マクロタスクの処理へ移る

1. `task1` 実行 → `console.log('task1')` → `task1`
2. `resolve()` 呼び出し → Promise 解決 → `.then(job1)` 登録 → マイクロタスク1

マイクロタスク処理開始

1. `job1` 実行 → `console.log('job1')` → `job1`
2. `setTimeout(task2)` 登録 → マクロタスク2
3. `.then(job2)` 登録 → マイクロタスク2
4. `.then(job3)` 登録 → マイクロタスク3

マイクロタスク継続処理

1. `job2` 実行 → `console.log('job2')` → `job2`
2. `job3` 実行 → `console.log('job3')` → `job3`

ここでマイクロタスクが空 → 次のマクロタスク（`task2`）へ移る

1. `task2` 実行 → `console.log('task2')` → `task2`
2. `queueMicrotask(job4)` 登録 → マイクロタスク4

マクロタスク終了 → マイクロタスク実行

1. `job4` 実行 → `console.log('job4')` → `job4`


<br>

#### 練習問題-2

```jsx
console.log('start');

setTimeout(() => {
  console.log('timeout1');

  Promise.resolve().then(() => {
    console.log('micro1');
  });

  setTimeout(() => {
    console.log('timeout2');
  });

}, 0);

Promise.resolve().then(() => {
  console.log('micro2');

  return Promise.resolve().then(() => {
    console.log('micro3');
  });
}).then(() => {
  console.log('micro4');
});

queueMicrotask(() => {
  console.log('micro5');
});

console.log('end');

```

上記のコードがどのように動き、どの順番でコンソールに出力されるか考えてみて下さい。
答えが決まったらスクロールして下さい。
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>

回答
```
//コンソール出力
start  
end  
micro5  
micro2  
micro3  
micro4  
timeout1  
micro1  
timeout2
```


### コードの流れ（イベントループとタスクの順序）

1. `console.log('start')` → 同期 → `start`
2. `setTimeout(..., 0)` 登録 → マクロタスク1
3. `Promise.resolve().then(...)` 登録 → マイクロタスク1
    - `micro2`, さらに `.then(() => micro3)` → ネスト → マイクロタスク2
4. `.then(() => micro4)` → マイクロタスク3（micro3のあと）
5. `queueMicrotask(...)` 登録 → マイクロタスク4（`micro5`）
6. `console.log('end')` → 同期 → `end`

 ここで同期タスク終了 → マイクロタスクの処理へ移る

1. `micro5`（queueMicrotask） → `micro5`
2. `micro2` → `micro2`
3. `micro3`（`micro2`の中）→ `micro3`
4. `micro4`（`micro3`のあと）→ `micro4`

ここでマイクロタスクが空 → 次のマクロタスク（setTimeout）

1. `timeout1` → `timeout1`
2. `Promise.resolve().then(...)` → マイクロタスク登録（`micro1`）
3. `setTimeout(...)` → マクロタスク登録（`timeout2`）

マクロタスク内のマイクロタスク実行

1. `micro1`
2. `timeout2`




## 非同期の並列処理

- `Promise`を**直列につなぐ処理**は「Promiseチェーン」と呼ばれる
- 一方、**複数のPromiseを同時に実行**するのが「並列処理」
    
    `Promise.all()` や `Promise.race()` を使って複数の非同期処理を同時に扱えます。
    

 ### Promise.all(反復可能オブジェクト)

```jsx
function sleep(val) {
  return new Promise(function (resolve,reject) {
    setTimeout(function () {
      console.log(val++);
      resolve(val);
    }, val * 1000);
  });
}

Promise.all([sleep(1), sleep(2), sleep(3)])
	.then(function(data) {
   console.log(data);
})

//コンソール出力
1
2
3
(3) [2, 3, 4]
```
### コードの流れ

 `sleep`関数 

- `sleep(val)` は `Promise` を返す関数
- `setTimeout` を使って `val` 秒後に処理を実行する（例：`sleep(2)`なら2秒後）
- `console.log(val++)` によって現在の値を出力し、次に使うためにインクリメント
- `resolve(val)` により、インクリメントされた値を次の `.then()` に渡す

 `Promise.all()`

- `Promise.all([...])` は、配列内の**すべての Promise が「成功」するのを待ってから**次の `.then()` に進む
- すべてが完了すると、**各 `resolve` の値が配列となって渡される**

<br>
<br>

### Promiseチェーンの戻り値にPromise.all([...])を渡した場合

```jsx
sleep(1).then(function(val) {
  return (Promise.all([sleep(2), sleep(3), sleep(4)]))
}).then(function(val) {
  console.log(val)
  return sleep(val[1]);
}).then(function(val) {
  return sleep(val);
})

//コンソール出力
1
-------
2   ↑
3   並列
4   ↓
-------
(3) [3, 4, 5]
4
5
```

### コードの流れ

1. `sleep(1)` により 1秒後に `1` を出力、`resolve(val)` には 2 が渡る
2. `.then()` で `Promise.all([sleep(2), sleep(3), sleep(4)])` を返す：
    - この3つの `sleep()` は**並列**でスタート
    - それぞれ `2s`, `3s`, `4s` 後に `2`, `3`, `4` を出力（インクリメントされるので `[3, 4, 5]` を返す）
3. `.then()` に渡される `val` は `[3, 4, 5]`。その配列の `val[1]` = `4` を次の `sleep()` に渡す
4. `sleep(4)` → 4秒後に `4` を出力、val++ → `5` を返す
5. `sleep(5)` → 5秒後に `5` を出力

### 補足

- `Promise.all([...])` で並列処理
    - **配列の各要素は `Promise` を返す必要がある。**
    - すでに解決済みの値[1,2,3]（同期処理）を渡しても内部で `Promise.resolve()` に包まれる（`Promise.all([1, 2, 3])` は `Promise.all([Promise.resolve(1), ...])` と同じ動作になる）ため`.then()` は実行される。ただし、`Promise.all()` の本来の目的は**非同期処理の並列実行**なので、通常は非同期関数の `Promise` を渡す
- `sleep().then()` のようにつなぐと直列処理
- 複雑なフローでも `.then()` チェーンをうまく使えば、**直列→並列→直列** のような処理が実現できる

### Promise.race(反復可能オブジェクト)

```jsx
//・・・sleep関数、ボタンイベントは省略
Promise.race([sleep(1),sleep(2),sleep(3)])
.then(function (data) {
  console.log(data)
})

//コンソール出力
1
2
2
3
```

### コードの流れ

 `Promise.race()`

- `Promise.race([...])` は、配列内で**一番最初に完了（解決または拒否）した Promise の結果**に応じて、次の `.then()` または `.catch()` に進む
- 一番早く完了した `Promise` の**結果（値またはエラー）**が、`.then()` または `.catch()` に渡される

### Promise.allSettled(反復可能オブジェクト)

```jsx
//shouldReject = false/trueでresolve/rejectを切り替え
function sleep(val, shouldReject = false) {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      console.log(val++);
      if (shouldReject) {
        resolve(val);
      } else {
        reject(val);
      }
    }, val * 1000);
  });
}

Promise.allSettled([sleep(1),sleep(2),sleep(3)])
.then(function (data) {
  console.log(data)
}).catch(function(e) {
  console.error(e)
})

//コンソール出力 
resolve(val)
1
2
3
(3) [{…}, {…}, {…}]
{status:'fulfilled', value:2}
{status:'fulfilled', value:2}
{status:'fulfilled', value:3}

reject(val)
1
2
3
(3) [{…}, {…}, {…}]
{status:'rejected', reason:2}
{status:'rejected', reason:2}
{status:'rejected', reason:3}
```

### コードの流れ
 `Promise.allSettled()`

- `Promise.allSettled([...])` は、配列内の**すべての Promise が完了（成功・失敗どちらでも）するのを待ち、必ず次の `.then()` に進む**
- すべての処理が終わると、結果は各 Promise の状態を表すオブジェクトの配列で返される
- 各オブジェクトは成功時、 { status: 'fulfilled', value: 結果 }、失敗時、{ status: 'rejected', reason: エラー内容 } の形になっているため、status プロパティを確認して成功・失敗を判別する
    






## 最後に
いかがだったでしょうか？
この記事を読む前よりも、非同期処理について少しでも理解が深まったと感じてもらえたら嬉しいです。

すぐ完璧に理解できなくても大丈夫。
非同期処理は一度でスッと理解できるものではありません。繰り返し手を動かしながら学習することで、必ず身についていきます。

実を言うと、僕自身も2年前まではPC操作すらままならず、最近まで非同期処理が苦手すぎて「なんとなく避けて」きました。
でも、何度もつまずきながらも少しずつ向き合っていくうちに、やっと「わかった！」と思える瞬間がやってきました。
非同期処理が分かるようになると、JavaScriptでの開発がぐっと楽しくなります。
「苦手だな…」と感じていたことが「おもしろい！」に変わる瞬間を、ぜひ体験してみてください。

もし、もっとJavaScriptの本質や仕組みを深く学びたいと思った方は、参考書籍や教材にチャレンジしてみるのもおすすめです。
（ただし、ただ、決して難易度は低くないです。焦らずじっくり取り組んでくださいね！）


## 参考教材
[【JS】ガチで学びたい人のためのJavaScriptメカニズム](https://www.udemy.com/share/103dh43@vDJbBYIlgfH5z9YJmRZvT9Ep79oUkzrgTooWYw_An_utOndh8C7trUaxMqID2HGX_w==/)