【初心者用】ちょっとだけPromiseに対しての理解を深めるための記事

# Promiseにちょっとだけ詳しくなろう
非同期処理にも慣れてきたぞ！という方のPromiseに対する理解をすこしだけアップデートさせる記事にしました。

各メソッドの違いや使用どころはシンプルに紹介していますので、理解した上で各々のプロジェクトで活かしてもらいたいです。

# `Promise.all()`と`fetchSequentially()`

### Promise.all :fire:
こちらはほとんどの方が知っていると思いますが、後のメソッドと比較するためにあえて記載します。`Promise.all`は複数の非同期処理を**並列**で実行し、すべての処理が完了するのを待ってから結果を返します。

```js
const urls = [
  'https://api.example.com/data1', 
  'https://api.example.com/data2',
  'https://api.example.com/data3'
];

Promise.all(urls.map(url => fetch(url).catch(err => err)))
  .then(results => {
    console.log(results); // 成功した処理の結果が全て入る
  })
  .catch(error => {
    console.error(error); // 失敗した処理のエラーが入る
  });
```


**利点**
- 並列で処理できるので実行時間が短縮される可能性がある

**欠点**
- 処理の実行順序は保証されない


### fetchSequentially() :chains:
fetchSequentiallyは「順次」って意味なので、それを考えると処理の内容はそのまんまになってます。つまりPromise.all()の並列処理に対して`fetchSequentially()`は複数の非同期処理を**直列**実行する関数です。1つの非同期処理が完了してから次の処理に移ります。

```js
const urls = [
  'https://api.example.com/data1', 
  'https://api.example.com/data2',
  'https://api.example.com/data3'
];

function fetchSequentially(urls) {
  const results = [];
  const fetching = urls.reduce((promise, url) => {
    return promise.then(() => 
      fetch(url).then(res => results.push(res)).catch(err => results.push(err))
    )
  }, Promise.resolve());
  return fetching.then(() => results);
}

```

- `reduce`メソッドで非同期処理を連鎖させ、前の処理が完了してから次の処理に移ります
- `reduce` の初期値として `Promise.resolve()` を使用してます。これで、最初のPromiseがすぐに実行されて最初のURLのフェッチがトリガーされます。

**利点**
- 処理の実行順序が保証される
- 1つの処理の完了を待ってから次に進むので、並列処理に比べてリソース消費が少ない

**欠点**
- 処理全体の実行時間が長くなる可能性がある


### まとめ
`fetchSequentially()`は処理の順序を守りたい場合や、リソース消費を気にする場合に適しています。一方`Promise.all`は高速な処理が求められる場合に適しています。

また、ここまで紹介したPromise.all()とfetchSequentially()は次に紹介する`Promise.allSettled()`の違いを分かりやすくするためにcatch文でエラーハンドリングしてます。
Promise.all()やfetchSequentially()は配列内のどれか一つでもPromiseがrejectされるとすぐにエラーになるので、**catchブロックがない場合はエラーが発生した時点で処理が停止します。**

これを踏まえて次の`Promise.allSettled()`をみていきましょう。

# Promise.allSettled() :a:

Promise.allSettled()は、複数のPromiseがすべて完了するのを待ってから、その結果を配列で返します。そして、その結果は**Promiseが解決された場合も拒否された場合も含まれます。** つまり1つ失敗してもすべての結果を取得できるということです。**catch文を書く必要がなく、Promiseの状態(fulfilled/rejected)を区別してくれます。**

```js
const fetchData1 = fetch('/data1.json');
const fetchData2 = fetch('/data2.json');
const fetchData3 = fetch('/data3.json');

Promise.allSettled([fetchData1, fetchData2, fetchData3])
  .then(results => {
    results.forEach(result => {
      if (result.status === 'fulfilled') {
        console.log('Success:', result.value);
      } else {
        console.log('Error:', result.reason);
      }
    });
  });
```
コード自体は簡単で、3つの異なるAPIリクエストを送信し、すべての結果を待ってから、成功した場合はデータを、失敗した場合はエラーメッセージを出力してます。


# Promise.race() :rocket:
Promise.race()は、読んで字の如くレースです。
複数のPromiseのうち最初に完了したPromiseの結果を**成功、失敗関係なく返します。**
そして、他のPromiseは無視されます。

```js
import { timeout } from 'promise-timeout-utilities';
const fetchDataFromServer1 = fetch('/data.json');
const fetchDataFromServer2 = fetch('https://backupserver.com/data.json');

Promise.race([
fetchData1,
fetchData2,
timeout(5000, 'Request timed out')
]).then(response => console.log(response)).catch(error => console.error(error));
```
こちらもコード自体は簡単で、最初に完了したPromiseの結果が使われて、それ以外のPromiseは無視されるようになってます。


# はしょったまとめ
**Promise.all()**・・・複数の非同期並列処理。catch文書かないと処理失敗したら止まる
**fetchSequentially()**・・・複数の非同期直列処理。catch文書かないと処理失敗したら止まる
**Promise.allSettled()**・・・複数の非同期並列処理。catch文書かなくても成功か失敗か仕分けして返してやるよ！
**Promise.race()**・・・複数の非同期並列処理。成功、失敗なんて関係ねぇ！一番早く終わった結果を返すぞ！