JavaScript の fetch API は HTTP リクエストの作成に広く使用されていますが、なぜ 2 つの await ステートメントが必要になるのかを理解するのは少し難しい場合があります。以前にフェッチを使用したことがある場合は、次のようなコードに遭遇したことがあるかもしれません:
const response = await fetch('https://api.example.com/data');
const data = await response.json();
ログイン後にコピー
ログイン後にコピー
これを分解して、なぜこのパターンが必要なのかを理解しましょう。 ?
フェッチの 2 段階のプロセス ?️
フェッチ API はネットワーク リクエストを非同期的に処理するように設計されていますが、その動作は 2 つの主要な段階に分かれています。
-
応答を取得しています ?
- fetch を呼び出すと、ネットワーク リクエストが完了すると、Response オブジェクトに解決される Promise が返されます。
- このステップでは応答の本文は処理されません。リクエストが成功し、ヘッダーが利用可能であることを確認するだけです。
-
レスポンスボディを読み取る ?
- Response オブジェクトには、実際のコンテンツを読み取るための .json()、.text()、.blob() などのメソッドがあります。
- 本文の読み取りは非同期であるため、これらのメソッドは Promise も返します。これは、メインスレッドをブロックせずに大きなペイロードを効率的に処理するために必要です。
最初の待機中に何が起こるでしょうか? ⏳
const response = await fetch(url); と書くと、次のことが起こります:
-
ネットワークリクエストが送信されました: ?
- ブラウザは、指定された URL への HTTP リクエストを開始します。
- これには、ドメイン名の解決、TCP 接続の確立、HTTP ヘッダーと本文の送信 (POST リクエストの場合) が含まれます。
-
受信した応答メタデータ: ?
- サーバーがステータス行 (HTTP/1.1 200 OK など) とヘッダーで応答すると、フェッチ呼び出しは解決されます。この時点で:
- ステータス (例: 200、404、または 500) および statusText (例: "OK" または "Not Found") が利用可能です。
- Content-Type、Content-Length などの応答ヘッダー、およびサーバーによって送信されたカスタム ヘッダーにアクセスできます。
-
応答オブジェクトが作成されました: ?️
- ブラウザは、応答に関するメタデータを含む Response オブジェクトを構築します。これには以下が含まれます。
-
ヘッダー: response.headers 経由でアクセスでき、Content-Type や Authorization などの特定のヘッダーを検査できます。
-
本文: この時点では、本文は完全には読み取られず、解析もされておらず、読み取り可能なストリームのままです。
たとえば、サーバーが次を返した場合:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 123
{"message": "Hello, world!"}
ログイン後にコピー
Response オブジェクトには以下が含まれます:
-
ステータス: 200 ✅
-
ステータステキスト: "OK" ✅
-
headers: 応答ヘッダーの反復可能なコレクション (例: Content-Type: application/json)。
-
body: まだ解析されていない読み取り可能なストリーム。
2 番目の待機中に何が起こるでしょうか? ?
const data = await response.json(); と記述すると、次の手順が実行されます。
-
本文ストリーム読み取り: ?
- 応答の本文 (生の形式のまま) はストリームとして読み取られます。使用する方法に応じて、生データは次のように処理されます。
-
.json(): ストリームを JSON として解析し、JavaScript オブジェクトを返します。
-
.text(): ストリームをプレーン テキスト文字列として読み取ります。
-
.blob(): ストリームをバイナリ ラージ オブジェクトとして読み取ります。
-
解析と解決: ?
- json() メソッドは、生データ (例: {"message": "Hello, world!"}) を解析して使用可能な JavaScript オブジェクト (例: { message: "Hello, world!" }) に変換します。
- この解析プロセスには、潜在的に大きなデータの処理が含まれるため、非同期です。
-
約束の解決策: ✅
- response.json() によって返された Promise は解析されたデータに解決され、アプリケーションで使用できるようになります。
なぜ 2 つの await ステートメントが必要なのでしょうか?
2 回待つ必要がある理由は次のとおりです:
-
最初に待機 (応答待ち):
- フェッチ呼び出しでは、応答データがすぐには提供されません。それはあなたに約束を与えます。 Response オブジェクトを取得するには、それを待つ必要があります。
-
2 番目の待機 (本体の解析):
- .json() メソッド (またはその他の本文読み取りメソッド) は別の Promise を返します。解析されたコンテンツを抽出するには、これを待つ必要があります。
どちらかの await をスキップすると、予期しない動作が発生する可能性があります:
-
最初の await をスキップします: 実際の Response オブジェクトではなく、未解決の Promise を操作することになります。
-
2 番目の待機をスキップします: 解析されたデータの代わりに Promise を取得します。
エラー処理の例 ?️
フェッチの操作中にエラーを適切に処理する方法は次のとおりです。
const response = await fetch('https://api.example.com/data');
const data = await response.json();
ログイン後にコピー
ログイン後にコピー
よくある落とし穴 ⚠️
-
エラーを処理していません:
-
fetch は、404 や 500 などの HTTP エラーに対してエラーをスローしません。response.ok または response.status を手動で確認する必要があります。
-
2 番目の待機をスキップします:
- .json() の await を忘れると、実際のデータではなく Promise を操作することになり、バグが発生する可能性があります。
-
フェッチ API と古い API の間の混乱:
- XMLHttpRequest などの古い API から移行する開発者は同期動作を期待するかもしれませんが、フェッチは完全に Promise ベースです。
結論 ?
fetch で 2 つの await ステートメントを使用するのは、最初は冗長に思えるかもしれませんが、これは非同期設計の論理的な結果です。最初の await は、ヘッダーとメタデータを含む応答が受信されたことを確認し、2 番目の await は応答本文を処理します。このフローを理解すると、より信頼性が高く保守しやすい非同期コードを作成するのに役立ちます。 ?
以上がフェッチに 2 回待機する必要がある理由を理解する✨の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。