fetch:拒绝带有 JSON 错误对象的承诺

Posted

技术标签:

【中文标题】fetch:拒绝带有 JSON 错误对象的承诺【英文标题】:fetch: Reject promise with JSON error object 【发布时间】: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
   

【问题讨论】:

你的意思是json 方法返回一个Promise? 是的,根据工作组的fetch 规范:fetch.spec.whatwg.org/#concept-body-consume-body 【参考方案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=***lyn,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=***lyn,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。

以上是关于fetch:拒绝带有 JSON 错误对象的承诺的主要内容,如果未能解决你的问题,请参考以下文章

Javascript Fetch API - 如何将输出作为对象保存到变量(不是承诺)

*可能的未处理承诺拒绝(id:0):类型错误:未定义不是对象(评估'result.cancelled')云图像上传

处理带有承诺的对象数组

.catch() 甚至通过 fetch() 从另一个承诺中捕获所有错误

使用React Native Fetch中的对象响应时未处理的Promise Rejection

未处理的承诺拒绝:未定义不是对象(评估 _expoLocation.requestForegroundPermissionsAsync)