【问题标题】:How to catch an error thrown in XMLHttpRequest's onload method如何捕捉 XMLHttpRequest 的 onload 方法中抛出的错误
【发布时间】:2019-06-02 13:56:16
【问题描述】:

我是 throw 处理我的 XMLHttpRequest 对象的 onload 方法的异常。但是我无法使用try catch 块来catch 那个错误。

我的_fetch(url, callback) 函数中有以下代码:

const xhr = new XMLHttpRequest()

xhr.onload = function () {
  if (xhr.readyState === xhr.DONE) {
    // if (xhr.status === 200) callback(xhr.response)

    // throw a 'custom' error if the server responds with 501
    if (xhr.status === 501) throw new Error('Answer not found') /
  }
}

// rest of the fn

然后,我正在使用_fetch 函数:

try {
  _fetch(endPoint, response => console.log(response))
} catch (error) { // error not caught
  console.log(error)
}

但是,错误并没有被捕获,而是抛出了一个错误——Uncaught Error: Answer not found

我尝试过的:

  • try catchinging xhr 对象的 send() 方法在 _fetch
  • try catching_fetchxhr 对象的 open() 方法@
  • 使用 xhr 对象的 onerror 方法 - 将不起作用,因为请求已成功完成

那么,我怎么能catch那个错误呢?

要试用完整代码,请查看this fiddle。


为什么需要throwonload方法中的错误?

我正在从某个 API 获取数据,由于某些限制,我无法使用 fetch。我改用XMLHttpRequest。如果我发送的查询无法回答,服务器会返回错误代码501 - Not Implemented。 因此,我需要挑选出那个错误代码,并执行一些功能。

显然,还有其他可能的方法可以实现我的最终目标,但我发现很难理解为什么这不起作用。我已经阅读了throwXMLHttprequest,但我可能对其中一个的工作原理存在误解。

【问题讨论】:

    标签: javascript ajax xmlhttprequest


    【解决方案1】:

    您绝对可以使用 fetch 不仅让您自己更轻松,还可以处理错误。此代码下方的 JSFiddle 链接。

    const url = `http://api.wolframalpha.com/v1/result?appid=H2R834-AUAJXG9KRV&i=%3F`
    fetch(`https://cors-anywhere.herokuapp.com/${url}`)
    .then(res => {
        console.log(res.status)
        if (res.status == 200) {
            return res.json()
        }
        else if (res.status == 501) {
            throw new Error("Answer not found")
        }
        else {
            throw new Error("Some other status code")
        }
    })
    .then(json => { 
        console.log(json) 
    })
    .catch(err => { 
        console.error(err)
    })
    

    https://jsfiddle.net/mparson8/ug1hoks8/9/

    【讨论】:

    • 嘿,谢谢,这行得通。我一定有一些疏忽(当然是在试图绕过 CORS 限制时)。尽管如此,我还是不明白为什么我无法从 onload 方法中捕获错误。很高兴能理解这一点。在此期间,我将切换到 fetch。
    • Fetch 功能强大、灵活、易于使用,如果您担心浏览器兼容性,可以使用 polyfill。话虽如此,我的理论是,由于您在另一个函数内的函数内抛出错误,因此错误不会被提升到外部 _fetch 函数中,因此 try..catch 块不能做任何事情它。通过回调 like in this JSFiddle with your code
    【解决方案2】:

    虽然您可以使用库让您的生活更轻松,但

    XMLHttpRequest onXXXX 实际上是事件处理程序,onload 只是许多可能的事件之一。完整列表在这里:https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#Events

    在这种情况下,您缺少错误处理程序:

    xhr.onerror( ... )

    另外,如果您可以使用现代浏览器,请使用 xhr.addEventListener('load'|'error'|..., ... ) 代替。这实际上允许多个处理程序,删除处理程序等。


    关于您的示例代码:

    function _fetch(url, callback)

    
    const xhr = new XMLHttpRequest()
    
    xhr.onload = function () {
      if (xhr.readyState === xhr.DONE) {
        // if (xhr.status === 200) callback(xhr.response)
    
        // throw a 'custom' error if the server responds with 501
        if (xhr.status === 501) throw new Error('Answer not found')
        // This can never be caught because async functions break the stack.
        // Intuition: If you can't return it, you can't throw either.
      }
    }
    
    // rest of the fn
    

    然后,我正在使用 _fetch 函数:

    try {
      _fetch(endPoint, response => console.log(response))
    } catch (error) { // error not caught
      console.log(error)
    }
    

    在这种情况下,您需要修改 fetch 的回调以使用两个参数,在 similar style to node.js

    特别是:_fetch(endPoint, (err, resp) => ... )

    
    xhr.onload = function () {
      if (xhr.readyState === xhr.DONE) {
        // if (xhr.status === 200) callback(null, xhr.response)
    
        // throw a 'custom' error if the server responds with 501
        if (xhr.status === 501) callback(new Error('Answer not found'), null);
      }
    }
    

    当然,聪明的方法是使用 Promise(或库)。确保至少调用一次 resolve 或 reject。

    function _fetch(endpoint, callback) {
       return new Promise( (resolve, reject) => {
          xhr.onload = function () {
            if (xhr.readyState === xhr.DONE) {
            // if (xhr.status === 200) resolve(xhr.response)
    
            // throw a 'custom' error if the server responds with 501
            if (xhr.status === 501) reject(new Error('Answer not found'));
            }
          }
    
       });
    }
    

    这允许您按如下方式使用它:

    _fetch(endpoint)
    .then ( res => console.log('result', res) )
    .catch( e => console.log('error', e);
    

    【讨论】:

    • fetch 不需要使用外部库。因此,它内置在 ECMAScript 6 和所有现代浏览器中。而且它比XMLHttpRequest 更容易使用(也更强大)。我建议您熟悉fetch,因为您再也不想使用XMLHttpRequest了!
    • @MarcusParsons 同意,我确实忘记了。再说一次,它仍然被认为是实验性的。我实际上推荐库而不是实验性浏览器功能。这样它们就可以在浏览器/node.js 中始终如一地工作,并且总体上更方便(例如,自动将 JSON 字符串化)。
    • MDN 有一个 polyfill 支持旧版浏览器,这样您就可以保持fetch 的易用性。 polyfill 基本上只是XMLHttpRequest 的语法糖。但是,我觉得我现在正在和我的狗说话,因为我已经说了很多次fetch 哈哈
    • 嘿@blackening。感谢您的提示,事件监听器的胜利!正如我已经澄清的那样,使用 xhr 对象的 onerror 方法将不起作用,因为请求已成功完成(onerror 在小提琴中被注释掉)。在服务器 501 错误的情况下不会在 xhr 中触发错误。
    • 哦,字面上的“捕获”?我稍后会更新答案。基本上你需要正确回调。您不能在回调中抛出并期望捕获它。要么返回回调错误节点样式,要么创建一个承诺并拒绝它。
    猜你喜欢
    • 1970-01-01
    • 2019-12-06
    • 2015-03-12
    • 1970-01-01
    • 2014-03-02
    • 2022-01-26
    • 2020-03-18
    • 1970-01-01
    • 2020-08-24
    相关资源
    最近更新 更多