【问题标题】:fetch: Reject promise with JSON error objectfetch:拒绝带有 JSON 错误对象的承诺
【发布时间】:2015-06-10 23:28:00
【问题描述】:

我有一个 HTTP API,它在成功和失败时都返回 JSON 数据。

示例失败如下所示:

~ ◆ http get http://localhost:5000/api/isbn/2266202022 
HTTP/1.1 400 BAD REQUEST
Content-Length: 171
Content-Type: application/json
Server: TornadoServer/4.0

{
    "message": "There was an issue with at least some of the supplied values.", 
    "payload": {
        "isbn": "Could not find match for ISBN."
    }, 
    "type": "validation"
}

我想在我的 JavaScript 代码中实现的是这样的:

fetch(url)
  .then((resp) => {
     if (resp.status >= 200 && resp.status < 300) {
       return resp.json();
     } else {
       // This does not work, since the Promise returned by `json()` is never fulfilled
       return Promise.reject(resp.json());
     }
   })
   .catch((error) => {
     // Do something with the error object
   }

【问题讨论】:

标签: javascript promise es6-promise fetch-api


【解决方案1】:
 // This does not work, since the Promise returned by `json()` is never fulfilled
return Promise.reject(resp.json());

好吧,resp.json 的承诺被实现,只有 Promise.reject 不等待它并立即拒绝承诺

我假设您更愿意执行以下操作:

fetch(url).then((resp) => {
  let json = resp.json(); // there's always a body
  if (resp.status >= 200 && resp.status < 300) {
    return json;
  } else {
    return json.then(Promise.reject.bind(Promise));
  }
})

(或,明确写)

    return json.then(err => {throw err;});

【讨论】:

  • 谢谢,这(几乎)确实有效!我必须将 Promise.reject 包装在一个匿名函数中,否则我会得到一个 undefined is not a function 错误,但只要稍作改动,它就可以工作:-)
  • 呃,你是在延迟加载 Promise shim 吗?原生的Promise.reject 不应该是undefined
  • shim 在应用程序启动时加载,因此不应延迟加载。我也可以从调试器访问Promise.reject。这是完整的跟踪:TypeError: undefined is not a function {stack: "TypeError: undefined is not a function↵ at reject (native)", message: "undefined is not a function"}
  • 拒绝?啊,应该是.then(Promise.reject.bind(Promise))
  • @torazaburo:不,json 在这里是一个承诺,我们不想拒绝承诺,而是拒绝它的结果值。
【解决方案2】:

这是一种更简洁的方法,它依赖于 response.ok,并使用底层 JSON 数据,而不是 .json() 返回的 Promise

function myFetchWrapper(url) {
  return fetch(url).then(response => {
    return response.json().then(json => {
      return response.ok ? json : Promise.reject(json);
    });
  });
}

// This should trigger the .then() with the JSON response,
// since the response is an HTTP 200.
myFetchWrapper('http://api.openweathermap.org/data/2.5/weather?q=Brooklyn,NY').then(console.log.bind(console));

// This should trigger the .catch() with the JSON response,
// since the response is an HTTP 400.
myFetchWrapper('https://content.googleapis.com/youtube/v3/search').catch(console.warn.bind(console));

【讨论】:

  • 啊,.ok 看起来很有趣。然而,我没有看到“底层 JSON 数据”的使用更干净。毕竟,您可以将其简化为 fetch(url).then(response =&gt; response.ok ? response.json() : response.json().then(err =&gt; Promise.reject(err)))
  • 我的意思是,与 let json = resp.json(); 相比,jsonPromise,先解析 Promise 然后使用解析的数据可能更简单。两种方法都有效。
  • 试图拒绝嵌套的承诺,但不太确定如何。原来这只是对静态“拒绝”方法的调用。在我看来,这是一个比公认的更好的答案。
【解决方案3】:

上面来自Jeff Posnick 的解决方案是我最喜欢的方法,但是嵌套很丑。

使用较新的 async/await 语法,我们可以以一种更同步的方式执行此操作,而不会出现会很快变得混乱的丑陋嵌套。

async function myFetchWrapper(url) {
  const response = await fetch(url);
  const json = await response.json();
  return response.ok ? json : Promise.reject(json);
}

这是因为 an async function always returns a promise 并且一旦我们有了 JSON,我们就可以根据响应状态决定如何返回它(使用 response.ok)。

您会以与 Jeff 的回答相同的方式处理错误,但是您也可以使用 try/catch、error handling higher order function,或者进行一些修改以防止承诺被拒绝,您可以使用我最喜欢的技术来确保 @987654325 @。

const url = 'http://api.openweathermap.org/data/2.5/weather?q=Brooklyn,NY'

// Example with Promises
myFetchWrapper(url)
  .then((res) => ...)
  .catch((err) => ...);

// Example with try/catch (presuming wrapped in an async function)
try {
  const data = await myFetchWrapper(url);
  ...
} catch (err) {
  throw new Error(err.message);
}

同样值得一读MDN - Checking that the fetch was successful,了解为什么我们必须这样做,本质上,获取请求只会因网络错误而被拒绝,获得 404 并不是网络错误。

【讨论】:

    【解决方案4】:

    我在MDN找到了我的解决方案:

    function fetchAndDecode(url) {
      return fetch(url).then(response => {
        if(!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        } else {
          return response.blob();
        }
      })
    }
    
    let coffee = fetchAndDecode('coffee.jpg');
    let tea = fetchAndDecode('tea.jpg');
    
    Promise.any([coffee, tea]).then(value => {
      let objectURL = URL.createObjectURL(value);
      let image = document.createElement('img');
      image.src = objectURL;
      document.body.appendChild(image);
    })
    .catch(e => {
      console.log(e.message);
    });
    

    【讨论】:

      【解决方案5】:

      也许这个选项可能是有效的

      new Promise((resolve, reject) => { 
          fetch(url)
          .then(async (response) => {
              const data = await response.json();
              return { statusCode: response.status, body: data };
          })
          .then((response) => {
              if (response.statusCode >= 200 && response.statusCode < 300) {
                  resolve(response.body);
              } else {
                  reject(response.body);
              }
          })
      });
      

      【讨论】:

      • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
      猜你喜欢
      • 2017-01-20
      • 2017-03-12
      • 2016-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-06
      • 2022-12-04
      相关资源
      最近更新 更多