Почему .json() возвращает обещание?

Недавно я возился с API fetch() и заметил кое-что странное.

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.data возвращает объект Promise. 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 здесь - это стандартный Object которому вы можете получить доступ к атрибуту title. http://jsbin.com/wofulo/edit?js,output

Поэтому мой вопрос: почему response.json возвращает обещание в литерале объекта, но возвращает значение, если оно только что возвращено?

Ответ 1

Почему response.json возвращает обещание?

Потому что вы получите response как только все заголовки прибыли. Вызов .json() дает вам еще одно обещание для тела ответа http, который еще не загружен. См. Также Почему объект ответа из JavaScript API выборки является обещанием? ,

Почему я получаю значение, если я возвращаю обещание от then обработчика?

Потому что так обещают работать. Возможность возвращать обещания от обратного вызова и принимать их - их самая важная функция, которая делает их цепными без вложенности.

Ты можешь использовать

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

или любой другой подход к доступу к предыдущему обещанию приводит к цепочке .then() для получения статуса ответа после ожидания тела json.

Ответ 2

Это различие связано с поведением Promises больше, чем fetch().

Когда обратный вызов .then() возвращает дополнительный Promise, следующий обратный вызов .then() в цепочке по существу связан с этим обещанием, получая его разрешение или отклоняя выполнение и значение.

Второй фрагмент также может быть записан как:

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

Как в этой форме, так и в вашей, значение post обеспечивается обещанием, возвращенным из response.json().


Когда вы возвращаете простой Object, тем не менее, .then() считает, что успешный результат и немедленно разрешается, аналогично:

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

post в этом случае просто созданный Object, который содержит Promise в свойстве data. Ожидание выполнения этого обещания еще не завершено.

Ответ 3

Кроме того, что помогло мне понять этот конкретный сценарий, который вы описали, это документация Promise API, в частности, где объясняется, как обещание, возвращаемое методом then будет разрешаться по-разному в зависимости от того, что возвращает обработчик fn:

если функция-обработчик:

  • возвращает значение, обещание, возвращаемое потом, разрешается с возвращенным значением в качестве значения;
  • выдает ошибку, обещание, возвращаемое потом, отклоняется с брошенной ошибкой в качестве значения;
  • возвращает уже разрешенное обещание, возвращенное обещание затем разрешается с этим значением обещания в качестве его значения;
  • возвращает уже отклоненное обещание, а затем возвращенное обещание отклоняется с этим значением обещания в качестве его значения.
  • возвращает другой ожидающий объект обещания, разрешение/отклонение обещания, возвращенного к тому времени, будет следовать за разрешением/отклонением обещания, возвращенного обработчиком. Кроме того, значение обещания, возвращенного к тому времени, будет таким же, как значение обещания, возвращенного обработчиком.

Ответ 4

В дополнение к приведенным выше ответам, вы можете обработать ответ серии 500 от вашего api, где вы получите сообщение об ошибке, закодированное в json:

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
}