【问题标题】:error handling in Javascript promiseJavascript 承诺中的错误处理
【发布时间】:2016-03-02 06:32:57
【问题描述】:

我有一些 Javascript 代码用一些同步的后处理来做一些异步的东西,然后再做一些异步的东西(XHR -> 解析 XHR -> 基于第一个的新 XHR)。我没有完成错误处理:

/* API */
function getFile(name) {
    return $.ajax({
        url: name + ".json"
    }).then(function(data) {
        return data.id
    }, handleError)
}

function handleError(errorObj) {
    if (errorObj.status) {
        return errorObj.status
    } else {
        return errorObj
    }
}

function myApiCall() {
    return getFile(1)
        .then(getFile, handleError)
        .then(getFile, handleError)
        .then(getFile, handleError)
        .then(getFile, handleError);
}


/* caller */

function failFunction(status) {
    console.log("fail: ")
    console.log(status)
}

myApiCall().then(function(id) {
        console.log(id)
    }, failFunction)

1.json 看起来像这样

{
    "id": "2"
}

只引用下一个文件,其他的都是等价的。

直到这里一切都很好(即使我不确定这是否是进行错误处理的最佳方法)。如果所有文件都存在,则调用调用者的成功函数,否则调用错误函数。

但是当我的同步代码出现一些错误时,一切都会中断

function getFile(name) {
    return $.ajax({
        url: name + ".json"
    }).then(function(data) {
        throw new Error(42) //       <---------
    }, handleError)
}

现在我得到一个控制台输出

Error: 42

没有进一步处理,调用者不会被通知。

我尝试过类似的方法

function getFile(name) {
    return $.ajax({
        url: name + ".json"
    }).then(function(data) {
        throw new Error(42)
    }, handleError)
    .catch(handleError)
}

但这并没有使任何事情变得更好。最重要的是我得到一个TypeError: $.ajax(...).then(...).catch is not a function

在这种情况下如何正确解决错误处理?

【问题讨论】:

  • 如果您从 catch 或错误处理程序返回值,则错误不会传播 - 如果您希望它传播,请抛出错误或返回 Promise.reject。 jquery 函数可能会弄乱 Promise 链这一事实并不奇怪,jquery Promise 不是(至少在 v3 之前)真正符合 Promise/A+

标签: javascript jquery ajax promise


【解决方案1】:

致所有在使用 jQuery 时厌倦了 Promise 的人。 Promise 真的很酷; jQuery 承诺很烂。这里还有一些基于 Jaromandas 答案的示例代码,供任何可以使用的人使用。

/* API */
function getFileWithError(name) {
    return getFile(name, true)
}

function getFile(name, doThrow) {
  if (name == null) {
    console.log("handled null as return from chain before")
    name = 2
  }
  return wrapJquery(
    $.ajax({
      url: name + ".json?" + Math.random()
    })
  ).then(function(data) {
    if (data.id == 2) {
        return null
    }
    if (doThrow) {
        throw data.id
    }
    return data.id
  });
}

function wrapJquery(crossingFingersPromise) {
    return new Promise(function(resolve, reject) {
        crossingFingersPromise
        .then(function(data) {
          resolve(data);
        }, function(err) {
          reject(err);
        });
     })
}

function handleError(errorObj) {
    if (errorObj.status) {
        throw errorObj.status; // return in jquery!
    } else {
        throw errorObj;
    }
}

function myApiCall() {
    return getFile(1)
        .then(getFile)
        .then(getFileWithError)
        .then(getFile, function(error) {
            console.log("recovering from error")
            return getFile(error)
        })
        .then(getFile)
        .catch(handleError)
}
/* caller */
function failFunction(status) {
    console.log("fail: ");
    console.log(status);
}
myApiCall().then(function(id) {
    console.log(id);
})
.catch(failFunction);

【讨论】:

    【解决方案2】:

    因为你在每个阶段都在处理错误,而不是传播它们,所以你最终会遇到你遇到的问题

    编辑:

    上面的说法是错误的,我写的时候并没有意识到jquery promises是多么的糟糕——看看第一段代码(Promise/A+)和第二段代码(jQuery promise with finger crossed behind it back)之间的区别)

    相反,您可以执行以下操作:

    function getFile(name) {
      // wrap jquery "promise" in real Promise
      // you'll need a Promise polyfill for Internet Explorer to use this method
      return new Promise(function(resolve, reject) {
        $.ajax({
          url: name + ".json?" + Math.random()
        }).then(function(data) {
          resolve(data.id);
        }, function(err) {
          reject(err);
        });
      });
    }
    
    function handleError(errorObj) {
      //console.log(errorObj)
      if (errorObj.status) {
        throw errorObj.status;
      } else {
        throw errorObj;
      }
    }
    
    function myApiCall() {
      return getFile(1)
      .then(getFile)
      .then(getFile)
      .then(getFile)
      .then(getFile)
      .catch(handleError);
    }
    
    function failFunction(status) {
      console.log("fail: ")
      console.log(status)
    }
    
    myApiCall().then(function(id) {
      console.log(id);
    })
    .catch(failFunction);
    

    当使用 jQuery 时,它的承诺就像你在背后交叉手指做出的承诺

    function getFile(name) {
        return $.ajax({
            url: name + ".json"
        }).then(function(data) {
            return data.id;
        });
    }
    function handleError(errorObj) {
        if (errorObj.status) {
            return errorObj.status; // return in jquery!
        } else {
            return errorObj;
        }
    }
    function myApiCall() {
        return getFile(1)
            .then(getFile)
            .then(getFile)
            .then(getFile)
            .then(getFile)
            .fail(handleError); // like jquery promises in general, use fail instead of the compliant catch
    }
    /* caller */
    function failFunction(status) {
        console.log("fail: ");
        console.log(status);
    }
    myApiCall().then(function(id) {
        console.log(id);
    })
    .fail(failFunction);
    

    【讨论】:

    • 你试过了吗?粘贴你的代码对我来说有这样的效果,错误不会被转换为状态码,并且抛出新的 Error(42) 仍然会导致未捕获的异常......
    • Did you try that out? - 尽我所能,无需访问您的服务器和文件! the error isn't translated into a statuscode ... errorObj 是否有 .status - console.log 以找出...就未捕获的异常而言,您使用的是 jquery,祝它像 Promises 一样工作
    • 我刚刚测试了 jQuery 版本(第一个版本如果你在背后使用 jquery 交叉手指将无法工作)并且使用 .fail 等可以正常工作
    • 我已经更新了 first 代码块,用一个真正的承诺包装 jquery 对承诺的糟糕尝试——但是,这样,你需要一个 Promise polyfill (或提供某种承诺的其他库,不是 jquery)以使其在 Internet Explorer 中工作...... 答案中的所有代码都已经过测试,并且在两者中都可以按预期工作尽我所能“模拟”错误的非错误和错误条件如果它对您不起作用,则可能是缓存问题,或者您正在做其他完全错误的事情
    • 请避免使用 promise 构造函数反模式,只使用Promise.resolve($.ajax(…))。 jQuery 至少在 A+ 方面是可用的。
    猜你喜欢
    • 2020-07-01
    • 2021-02-08
    • 1970-01-01
    • 2020-08-17
    • 2018-06-01
    • 1970-01-01
    • 2015-07-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多