67

I have an HTTP API that returns JSON data both on success and on failure.

An example failure would look like this:

~ ◆ 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"
}

What I want to achieve in my JavaScript code is something like this:

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
   }
4

5 回答 5

102
 // 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;});
于 2015-04-06T16:52:21.417 回答
41

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

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));

于 2015-09-09T20:42:48.593 回答
11

上面来自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);
}

这是可行的,因为异步函数总是返回一个 Promise,一旦我们有了 JSON,我们就可以根据响应状态决定如何返回它(使用response.ok)。

您会以与 Jeff 的回答相同的方式处理错误,但是您也可以使用 try/catch,一个错误处理高阶函数,或者进行一些修改以防止承诺被拒绝,您可以使用我最喜欢的技术来确保错误处理是作为开发人员体验的一部分强制执行

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 - 检查获取是否成功以了解为什么我们必须这样做,本质上,获取请求只会因网络错误而拒绝,获得 404 不是网络错误。

于 2018-06-08T11:16:10.190 回答
0

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

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);
        }
    })
});
于 2022-01-13T22:07:54.787 回答
0

我在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);
});
于 2021-09-06T06:46:49.243 回答