为什么 .json() 返回一个 Promise,但通过 .then() 时却没有返回?

问题描述 投票:0回答:6

我最近一直在摆弄

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.data
返回一个
Promise
对象。 http://jsbin.com/wofulo/2/edit?js,输出

但是如果写成:

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,输出

所以我的问题是:为什么

response.json
在对象字面量中返回一个 Promise,但如果返回则返回值?

javascript asynchronous promise fetch-api
6个回答
282
投票

为什么

response.json
返回一个承诺?

因为一旦所有标头到达,您就会收到

response
。调用
.json()
会为您提供另一个尚未加载的 http 响应正文的承诺。另请参阅为什么 JavaScript fetch 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)
}));

或任何其他访问先前 Promise 的方法会导致 .then() 链在等待 json 主体后获取响应状态。使用 await

 的现代版本(在 
async
 函数内):

const response = await fetch(url); const data = await response.json(); console.log(response.status, data.title);
此外,您可能想检查状态(或只是

.ok

在阅读响应之前,它可能根本不是 JSON。


25
投票
这种差异是由于 Promise 的行为而不是

fetch()

 造成的。

.then()

 回调返回额外的 
Promise
 时,链中的下一个 
.then()
 回调本质上与该 Promise 绑定,接收其解析或拒绝履行和值。

第二个片段也可以写成:

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
。等待兑现的承诺仍然不完整。


7
投票
除了上述答案之外,这里还介绍了如何处理来自 api 的 500 系列响应,您会收到一条以 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 }
    

5
投票
此外,帮助我理解您描述的这个特定场景的是 Promise API

文档,特别是它解释了 then

 方法返回的承诺将如何根据 
handler fn 返回的内容以不同的方式解析:

如果处理函数:

    返回一个值:p 以返回值作为其值来实现。
  • 不返回任何内容:p 的值为 undefined 。
  • 抛出错误:p 被拒绝,并将抛出的错误作为其值。
  • 返回一个已经履行的承诺:p 得到履行,该承诺的值作为其值。
  • 返回一个已经被拒绝的 Promise:p 被拒绝,并以该 Promise 的值作为其值。
  • 返回另一个待处理的 Promise:p 处于待处理状态,并在该 Promise 被满足/拒绝后立即被满足/拒绝,并以该 Promise 的值作为其值。

0
投票
iterator .then(response => { return { data: response.json(), status: response.status } }) .then(post => document.write(post.data)); ;
当您使用 

data: response.json()

 时,您不会返回一个已解决/已拒绝的响应,该响应将是一个普通的 JS 对象或一个错误。由于 response.json() 返回一个承诺,并且在返回时您将获得已解决的状态。其中 post.data 是 
promise 对象

当您编写

return response.json()

 时,您可以使用返回的普通 JS 对象中的 title 属性访问已解析的响应。


-2
投票

json()方法可用于所有fetch()函数。 json() 方法返回一个 Promise。请记住,当返回 Promise 时,它仍然是 pending,因为它是异步的(假设数据还不存在)。因此,要使用 json() 方法获取数据 AFTER,您需要使用另一个 then() 方法,这样它只会在数据到达后返回数据。

回答你的问题,就是这样,只是这样做的方式。

就像 Promise ---> Another Promise ----> data

© www.soinside.com 2019 - 2024. All rights reserved.