awaitの必要性がわからない
この質問の本質が、仮に「fetch.json()するときに、なぜawaitを使うのか」ということとともに、「なぜ Fetch APIを使うときは、fetch()した後で、response.json() then というように2段構えにしないといけないのかわからない」であるとするならば、下記のような回答になります。
fetch()は、まずResponseに関するPromiseを返します。(「リクエストに対する Response に解決できる Promise を返します」)
const response = await fetch("URL");
await にて、解決した段階で、変数responseには、Responseオブジェクトが入ります。
Responseオブジェクトは、いくつかのメソッドを持っていますが、下記のように 色々なPromiseを返すメソッドを持っています。
引用元:https://developer.mozilla.org/ja/docs/Web/API/Response
[Responseオブジェクトが持つメソッドのうち、Promiseを返す代表的なメソッド]
+ Response.arrayBuffer()
レスポンスの本文を表す ArrayBuffer で解決するPromiseを返します。
+ Response.blob()
レスポンスの本文を表す Blob で解決するPromiseを返します。
+ Response.formData()
レスポンスの本文を表す FormData で解決するPromiseを返します。
+ Response.json()
レスポンスの本文のテキストを JSON として解釈した結果で解決するPromiseを返します。
+ Response.text()
レスポンスの本文のテキスト表現で解決するPromiseを返します。
json()メソッドは、上記のうちの1つです。
「レスポンスの本文のテキストを JSON として解釈した結果で解決するPromiseを解決した結果を受け取る」場合、一例として下記のように書けば実現できます。
const json = await response.json();
したがって、冒頭の質問に対する直接的な回答としては、
「Fetch APIは、fetch() で取得したPromiseを解決して一旦Responseオブジェクトを取得し、その取得したResponseオブジェクトに対して、様々な種類のPromiseを返すようなメソッドを持つ設計になっているから
(response.json()は、それらのうちの1つである)」
ということになります。
‘json()`が、JSONオブジェクトを直接返すメソッドではなく、Promiseを返すメソッドとなっていることのメリットは、JSON文字列のパース処理部分を(比較的簡単に)非同期処理として記述できる、という点だと思います。
これは誤りでした。
hoshi-takanoriさんが質問へのコメントに記載のstacocerflow内のコメントによると
「res.json() は res.text().then(JSON.parse) のショートカットとのこと。
hoshi-takanoriさんコメントのように、、「fetch 自体はレスポンスヘッダーが返ってきた段階 (XHR の readyState = 2) で resolve するが、response.json はボディ全体が返る (XHR の readyState = 4) までは resolve しない」となっていることのようですね。