web-dev-qa-db-ja.com

.json()が約束を返すのはなぜですか?

私は最近fetch() APIをめちゃくちゃにしていて、ちょっと風変わりなことに気づいた。

let url = "http://jsonplaceholder.typicode.com/posts/6";

let iterator = fetch(url);

iterator
  .then(response => {
      return {
          data: response.json(),
          status: response.status
      }
  })
  .then(post => document.write(post.data));
;

post.dataPromiseオブジェクトを返します。 http://jsbin.com/wofulo/2/edit?js,output

しかし、それが次のように書かれているなら:

let url = "http://jsonplaceholder.typicode.com/posts/6";

let iterator = fetch(url);

iterator
  .then(response => response.json())
  .then(post => document.write(post.title));
;

postは、title属性にアクセスできる標準のObjectです。 http://jsbin.com/wofulo/edit?js,output

だから私の質問です:なぜresponse.jsonはオブジェクトリテラルでpromiseを返すのですか?

79
haveacigaro

response.jsonが約束を返すのはなぜですか?

すべてのヘッダが到着するとすぐにresponseを受け取るからです。 .json()を呼び出すことで、まだロードされていないhttpレスポンスの本文に対する別の約束が得られます。また参照してください なぜJavaScriptからのレスポンスオブジェクトがAPIをフェッチするのか?

thenハンドラからpromiseを返した場合、なぜ値が返されるのですか?

なぜなら それが約束のしくみです 。コールバックからプロミスを返してそれらを採用させる機能は、それらの最も関連性の高い機能です。

あなたが使用することができます

fetch(url).then(response => 
    response.json().then(data => ({
        data: data,
        status: response.status
    })
).then(res => {
    console.log(res.status, res.data.title)
}));

またはその他の 以前の約束にアクセスするアプローチは.then()チェーンになります json本体を待った後に応答状況を取得するため。

120
Bergi

この違いは、特にfetch()以上のPromiseの振る舞いによるものです。

.then()コールバックが追加のPromiseを返すと、チェーン内の次の.then()コールバックは本質的にそのPromiseにバインドされ、解決または拒否の履行および値を受け取ります。

2番目のスニペットは次のように書くこともできます。

iterator.then(response =>
    response.json().then(post => document.write(post.title))
);

この形式でもあなたのものでも、postの値はresponse.json()から返されたPromiseによって提供されます。


ただし、プレーンなObjectを返すと、.then()は成功した結果と見なして次のように直ちに解決します。

iterator.then(response =>
    Promise.resolve({
      data: response.json(),
      status: response.status
    })
    .then(post => document.write(post.data))
);

この場合のpostは、単に作成したObjectであり、Promiseプロパティにdataを保持しています。その約束が満たされるのを待つのはまだ不完全です。

12

また、あなたが説明したこの特定のシナリオを理解する助けとなったのは、Promise API documentation です。具体的には、thenメソッドによって返されるpromiseが、 handler fnは以下を返します。

ハンドラー関数の場合:

  • 値を返します。それまでに返されたプロミスは、返された値をその値として解決されます。
  • エラーをスローすると、それまでに返されたプロミスは、スローされたエラーをその値として拒否されます。
  • 既に解決済みのプロミスを返します。そのとき返されるプロミスは、そのプロミスの値をその値として解決されます;
  • すでに拒否されたプロミスを返します。それまでに返されたプロミスは、そのプロミスの値をその値として拒否されます。
  • 別の保留中のpromiseオブジェクトを返します。thenによって返されたpromiseの解決/拒否は、ハンドラによって返されたpromiseの解決/拒否に続きます。また、thenによって返されるpromiseの値は、ハンドラーによって返されるpromiseの値と同じになります。
7
Gera Zenobi

上記の答えに加えて、ここにあなたがjsonでエンコードされたエラーメッセージを受け取るあなたのapiからの500シリーズのレスポンスをどのように扱うかもしれないかがあります:

function callApi(url) {
  return fetch(url)
    .then(response => {
      if (response.ok) {
        return response.json().then(response => ({ response }));
      }

      return response.json().then(error => ({ error }));
    })
  ;
}

let url = 'http://jsonplaceholder.typicode.com/posts/6';

const { response, error } = callApi(url);
if (response) {
  // handle json decoded response
} else {
  // handle json decoded 500 series response
}
5
jcroll