Tại sao .json () trả về một lời hứa?


115

fetch()Gần đây tôi đã làm rối tung lên với api và nhận thấy một điều gì đó hơi kỳ quặc.

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.datatrả về một Promiseđối tượng. http://jsbin.com/wofulo/2/edit?js,output

Tuy nhiên nếu nó được viết là:

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

let iterator = fetch(url);

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

postđây là một tiêu chuẩn Objectmà bạn có thể truy cập thuộc tính title. http://jsbin.com/wofulo/edit?js,output

Vì vậy, câu hỏi của tôi là: tại sao response.jsontrả về một lời hứa trong một đối tượng theo nghĩa đen, nhưng lại trả về giá trị nếu vừa được trả về?


1
Điều này có ý nghĩa khi bạn cho response.json()rằng lời hứa có thể bị từ chối nếu phản hồi không phải là JSON hợp lệ.
ssube

1
Giá trị được trả về bởi vì lời hứa đã được giải quyết chuyển giá trị trong response.json (). Bây giờ giá trị có sẵn trong phương thức then.
Jose Hermosilla Rodrigo,

Câu trả lời:


167

Tại sao phải response.jsontrả lại một lời hứa?

Bởi vì bạn nhận được responsengay sau khi tất cả các tiêu đề đã đến. Việc gọi điện cho .json()bạn một lời hứa khác cho phần nội dung của phản hồi http vẫn chưa được tải. Xem thêm Tại sao đối tượng phản hồi từ API tìm nạp JavaScript lại là một lời hứa? .

Tại sao tôi nhận được giá trị nếu tôi trả lại lời hứa từ thentrình xử lý?

Bởi vì đó là cách những lời hứa hoạt động . Khả năng trả lại lời hứa từ cuộc gọi lại và nhận chúng thông qua là tính năng phù hợp nhất của chúng, nó làm cho chúng có thể có được mà không cần lồng vào nhau.

Bạn có thể dùng

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

hoặc bất kỳ cách tiếp cận nào khác để truy cập lời hứa trước đó dẫn đến chuỗi .then () để nhận trạng thái phản hồi sau khi đã chờ nội dung json.


Có vẻ kỳ lạ là tôi không thể đợi dữ liệu trả về bằng Promise và khi nó đến thì chuyển đổi nó thành json? Hoặc có lẽ trong trường hợp đó tôi chỉ có thể sử dụng JSON.parse()thay vì res.json()??
Kokodoko

8
@Kokodoko res.json()về cơ bản là một phím tắt cho res.text().then(JSON.parse). Cả hai đều chờ dữ liệu bằng một lời hứa và phân tích cú pháp json.
Bergi

@Bergi, xin chào, xin lỗi, tôi đã gặp phải một số nhầm lẫn, đó là, bằng cách sử dụng then (res => res.json ()) chúng tôi gửi một yêu cầu khác để tải JSON?
mirzhal

1
@mirzhal Không, không có yêu cầu nào khác. Nó chỉ đang đọc (không đồng bộ!) Phần còn lại của phản hồi.
Bergi

14

Sự khác biệt này là do hành vi của Promises nhiều hơn là fetch()đặc biệt.

Khi một lệnh .then()gọi lại trả về một lệnh bổ sung Promise, lệnh .then()gọi lại tiếp theo trong chuỗi về cơ bản bị ràng buộc với Lời hứa đó, nhận giải quyết hoặc từ chối thực hiện và giá trị của nó.

Đoạn mã thứ 2 cũng có thể được viết là:

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

Trong cả biểu mẫu này và của bạn, giá trị của postđược cung cấp bởi Lời hứa được trả về từ đó response.json().


Tuy nhiên, khi bạn trả về một kết quả đơn giản Object, .then()hãy coi đó là kết quả thành công và tự giải quyết ngay lập tức, tương tự như:

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

posttrong trường hợp này chỉ đơn giản là Objectbạn đã tạo, giữ một tài sản Promisecủa nó data. Sự chờ đợi cho lời hứa đó được thực hiện vẫn chưa hoàn thành.


7

Ngoài ra, điều đã giúp tôi hiểu được kịch bản cụ thể mà bạn đã mô tả này là tài liệu Promise API , cụ thể trong đó nó giải thích cách mà thenphương thức đã hứa trả về sẽ được giải quyết khác nhau tùy thuộc vào những gì mà trình xử lý fn trả về:

nếu hàm xử lý:

  • trả về một giá trị, lời hứa được trả về sau đó được giải quyết với giá trị trả về là giá trị của nó;
  • ném một lỗi, lời hứa được trả về sau đó bị từ chối với giá trị của lỗi được ném ra;
  • trả về một lời hứa đã được giải quyết, lời hứa được trả về sau đó được giải quyết với giá trị của lời hứa đó là giá trị của nó;
  • trả về một lời hứa đã bị từ chối, lời hứa được trả lại sau đó bị từ chối với giá trị của lời hứa đó là giá trị của nó.
  • trả về một đối tượng hứa hẹn đang chờ xử lý khác, việc giải quyết / từ chối lời hứa được trả về sau đó sẽ tiếp theo việc phân giải / từ chối lời hứa được trình xử lý trả về. Ngoài ra, giá trị của lời hứa được trả về sau đó sẽ giống với giá trị của lời hứa được trình xử lý trả về.

5

Ngoài các câu trả lời ở trên, đây là cách bạn có thể xử lý phản hồi chuỗi 500 từ api của mình, nơi bạn nhận được thông báo lỗi được mã hóa bằng 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
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.