Чому .json () повертає обіцянку?


115

Я возився з 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якого можна отримати доступ до атрибута заголовка. http://jsbin.com/wofulo/edit?js,output

Отже, моє запитання: чому response.jsonповертає обіцянку в об'єкті буквально, а повертає значення, якщо тільки повертається?


1
Це має сенс, коли ви вважаєте, що response.json()обіцянка може бути відхилена, якщо відповідь не є дійсною JSON.
ssube

1
Значення повертається, оскільки обіцянку було вирішено, передаючи значення у response.json (). Тепер значення доступне в методі тоді.
Хосе Ермосіла Родріго

Відповіді:


167

Чому response.jsonповертає обіцянку?

Тому що ви отримуєте, responseяк тільки всі заголовки прибули. Зателефонувавши, .json()ви отримаєте ще одне обіцянку для тесту http-відповіді, яке ще потрібно завантажити. Дивіться також Чому об’єкт відповіді з API отримання файлу JavaScript є обіцянкою? .

Чому я отримую цінність, якщо повертаю обіцянку від 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.


Це здається дивним, що я не можу просто чекати повернення даних за допомогою Обіцянки, а коли він надійде, перетворити його в json? А може, в такому випадку я міг би просто використовувати JSON.parse()замість res.json()??
Кокодоко

8
@Kokodoko в res.json()основному це ярлик для res.text().then(JSON.parse). Обидва чекають даних за допомогою обіцянки та аналізують json.
Бергі

@Bergi, привіт, вибачте, я зіткнувся з деякою плутаниною, тобто, використовуючи тоді (res => res.json ()), ми надсилаємо ще один запит, щоб отримати JSON?
міржаль

1
@mirzhal Ні, іншого запиту немає. Він просто читає (асинхронно!) Решту відповідей.
Берги

14

Ця різниця обумовлена ​​поведінкою Обіцянок більш ніж 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власності. Чекання, коли ця обіцянка буде виконана, все ще не завершена.


7

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

якщо функція обробника:

  • повертає значення, повернута обіцянка потім вирішується з поверненою вартістю як його значення;
  • видає помилку, повернута обіцянка тоді відкидається з викинутою помилкою як її значення;
  • повертає вже розв’язану обіцянку, обіцянка, повернута до цього часу, вирішується зі значенням цієї обіцянки як її цінністю;
  • повертає вже відхилену обіцянку, повернуту обіцянку потім відхиляється із значенням цієї обіцянки як її цінності.
  • повертає ще один очікуваний об'єкт обіцянки, дозвіл / відхилення обіцянки, що повертається до того часу, буде наступним після вирішення / відхилення обіцянки, поверненої обробником. Також вартість обіцянки, повернутої на той час, буде такою ж, як і величина обіцянки, повернута обробником.

5

На додаток до вищенаведених відповідей, ось як можна обробити відповідь серії 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
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.