Promise、async/await、fetch APIを、1ステップずつ進むイベントループのタイムラインと、編集して実行できる15の例で解説します。JSON、フォーム、エラー、並列リクエスト、AbortController、リトライを扱います。
Promiseは、後で届く結果を表します。async関数はPromiseを返し、awaitはPromiseが確定するまでその関数を一時停止します。fetch(url)はResponseのPromiseを返します。response.okを確認してから、await response.json()を実行しましょう。awaitはtryとcatchで囲み、並列リクエストにはPromise.allを、キャンセルにはAbortControllerを使います。
ほとんどのページで必要なパターン
まずタイムラインで、awaitの後のコードがなぜそれより先に実行されることがあるのかを確認しましょう。その後、どの例でも編集するとその場で再実行されます。ここでのリクエストはdata: URLや疑似サーバーを使うため、ブラウザーの外には何も送信されません。
できることに違いはありません。async/awaitはPromiseの上に作られています。async関数は常にPromiseを返し、awaitはPromiseの中身を取り出します。普通の手順どおりのコードのように読めるため、tryとcatchでエラーを扱いやすくなります。
サーバーは応答を返しているので、fetchはどんなHTTP応答でもリクエストは成功したとみなします。rejectされるのは、ネットワーク障害かリクエストがキャンセルされた場合だけです。response.okやresponse.statusは自分で確認してください。
Promiseのコールバックはマイクロタスクキューに入り、現在のスクリプトの後に完全に空になるまで実行されます。タイマーのコールバックはタスクなので、イベントループの次の周回まで待ちます。
AbortControllerを作成し、fetchのオプションにcontroller.signalを渡して、controller.abort()を呼び出します。fetchのPromiseは、AbortErrorという名前のエラーでrejectされます。
はい、JavaScriptモジュール(script type="module"またはバンドルされたESモジュール)の中なら使えます。従来のスクリプトでは、コードをasync関数で囲みましょう。
まず同期的な行が実行され、次にキューに入ったすべてのPromiseのコールバック(マイクロタスク)、その後にタイマー(タスク)が実行されます。順番を変えて、もう一度実行してみましょう。
Promiseが確定するのは1回だけです。resolveは値を、rejectはエラーを渡します。thenとcatchは後で実行され、すぐには実行されません。
awaitが一時停止するのは、それが書かれたasync関数だけです。main()の呼び出しの次の行は、mainが終わる前に実行されます。
async関数の中では、rejectされたawaitが例外を投げるため、通常のtryとcatchが使えます。スピナーを隠すのはfinallyで行います。
fetchはResponseを返します。.json()は本文を読み取って解析するもので、これが2つ目のawaitになります。この例はdata: URLを使うのでオフラインでも動きます。
fetchがrejectされるのは、リクエスト自体ができない場合(オフライン、ブロック、不正なURL)だけです。404や500はokがfalseの状態で普通にresolveされるため、必ず確認しましょう。
FormDataは名前の付いたすべてのフィールドを読み取ります。ここではリクエストを作成しますが送信はせず、本文を表示して、サーバーが受け取る内容を正確に確認できるようにしています。
JSONを送るには、文字列化してContent-Typeヘッダーを自分で設定します。普通のオブジェクトに対して、fetchはどちらもやってくれません。
どのPromiseヘルパーを使うか
1回ずつawaitすると待ち時間が合計されます。すべてを開始してからPromise.allをawaitすれば、最も遅いものの分だけ待てば済みます。
Promise.allは1つでも失敗するとすぐにrejectされます。allSettledはすべてを待ち、どれが成功したかを教えてくれるので、独立したウィジェットのあるダッシュボードに向いています。
何百ものリクエストを一度に送るとレート制限に引っかかることがあります。この小さなプールは、同時に処理中のリクエストを最大3つに抑えます。
fetchにcontroller.signalを渡し、abort()を呼び出すと停止します。PromiseはAbortErrorでrejectされますが、通常は無視してかまいません。
AbortSignal.timeout(ms)は、自動的に中断するシグナルを作ります。fetchではエラー名がTimeoutErrorになるため、ユーザーによるキャンセルと区別できます。
リトライするのは、タイムアウトや503のように一時的な可能性があるエラーだけにし、負荷の高いサーバーが回復できるよう、毎回待ち時間を長くします。
入力が止まるのを待ち、新しいリクエストを開始するときに前のリクエストを中断します。こうすれば、遅れて届いた古い応答が新しい応答を上書きすることはありません。
読み取りたいレスポンスがありますか?貼り付けて確認できるのが
コールスタック
マイクロタスク
タスクキュー
コンソール
ステップ {i} / {n}。
戻る
次のステップ
一時停止
再生
リセット
無料で始める · クレジットカード不要