LoginSignup
0
1

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

Last updated at Posted at 2024-05-05

Promiseにちょっとだけ詳しくなろう

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

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

Promise.all()fetchSequentially()

Promise.all :fire:

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

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つの非同期処理が完了してから次の処理に移ります。

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)を区別してくれます。

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は無視されます。

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()・・・複数の非同期並列処理。成功、失敗なんて関係ねぇ!一番早く終わった結果を返すぞ!

0
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1