有了"承诺"之后,没完成,需要处理

共 4016字,需浏览 9分钟

 ·

2021-02-20 12:31

Promise 在错误处理方面非常出色。当 promise 拒绝时,控件跳转到最近的拒绝处理程序。这在实践中很方便。

例如,下面的代码中获取的URL是错误的(没有这样的站点),.catch处理错误:

fetch('https://no-such-server.blabla'// rejects
  .then(response => response.json())
  .catch(err => alert(err)) // TypeError: failed to fetch (the text may vary)

如您所见,.catch不一定是即时的。它可能出现在一个或几个 .then。

或者,可能站点一切正常,但响应不是有效的JSON。捕获所有错误的最简单方法是将.catch添加到chain的末尾:

fetch('/article/promise-chaining/user.json')
  .then(response => response.json())
  .then(user => fetch(`https://api.github.com/users/${user.name}`))
  .then(response => response.json())
  .then(githubUser => new Promise((resolve, reject) => {
    let img = document.createElement('img');
    img.src = githubUser.avatar_url;
    img.className = "promise-avatar-example";
    document.body.append(img);

    setTimeout(() => {
      img.remove();
      resolve(githubUser);
    }, 3000);
  }))
  .catch(error => alert(error.message));

通常,这种 .catch根本不会触发。但是,如果上面的任何一个 Promise 被拒绝(网络问题或无效的json或其他什么),那么它就会捕获它。

隐式 try catch

Promise 执行程序和 Promise 处理程序的代码有一个“不可见的 try..catch。如果发生了异常,它会被捕获并作为拒绝处理。

例如,以下代码:

new Promise((resolve, reject) => {
  throw new Error("Whoops!");
}).catch(alert); // Error: Whoops!

工作原理完全一样:

new Promise((resolve, reject) => {
  reject(new Error("Whoops!"));
}).catch(alert); // Error: Whoops!

“看不见的try..catch,执行程序会自动捕获错误并将其转换为被拒绝的Promise。

这不仅发生在executor函数中,也发生在其处理程序中。如果我们抛出一个.then处理程序,这意味着一个被拒绝的承诺,因此控件跳转到最近的错误处理程序。

这里有一个例子:

new Promise((resolve, reject) => {
  resolve("ok");
}).then((result) => {
  throw new Error("Whoops!"); // rejects the promise
}).catch(alert); // Error: Whoops!

所有错误都会发生这种情况,而不仅仅是由throw语句引起的错误。例如,一个编程错误:

new Promise((resolve, reject) => {
  resolve("ok");
}).then((result) => {
  blabla(); // no such function
}).catch(alert); // ReferenceError: blabla is not defined

最后的.catch不仅能捕获显式的拒绝,还能捕获上述处理程序中的意外错误。

Rethrowing

正如我们已经注意到的,.catch在链的末端类似于try..catch。我们可以有任意多的.then处理程序,然后在末尾使用一个.catch来处理所有处理程序中的错误。

在定期的尝试中…我们可以分析错误,如果不能处理,可能会重新抛出错误。同样的事情也可能发生在承诺上。

如果我们在.catch中抛出,那么控件将转到下一个最近的错误处理程序。如果我们处理错误并正常完成,那么它会继续到下一个成功的。then handler。

在下面的例子中,.catch成功地处理了错误:

// the execution: catch -> then
new Promise((resolve, reject) => {

  throw new Error("Whoops!");

}).catch(function(error{

  alert("The error is handled, continue normally");

}).then(() => alert("Next successful handler runs"));

在这里,.catch块正常完成。因此,调用下一个成功的.then处理程序。

在下面的例子中,我们将看到.catch的另一种情况。处理程序(*)捕获了错误,但无法处理它(例如,它只知道如何处理URIError),所以它再次抛出它:

// the execution: catch -> catch
new Promise((resolve, reject) => {

  throw new Error("Whoops!");

}).catch(function(error// (*)

  if (error instanceof URIError) {
    // handle it
  } else {
    alert("Can't handle such error");

    throw error; // throwing this or another error jumps to the next catch
  }

}).then(function({
  /* doesn't run here */
}).catch(error => { // (**)

  alert(`The unknown error has occurred: ${error}`);
  // don't return anything => execution goes the normal way

});

执行过程从第一个.catch(*)跳转到下一个。catch(**)。

Unhandled rejections

当错误没有被处理时会发生什么?例如,我们忘记将.catch添加到链的末尾,就像这样:

new Promise(function({
  noSuchFunction(); // Error here (no such function)
})
  .then(() => {
    // successful promise handlers, one or more
  }); // without .catch at the end!

在出现错误的情况下,承诺被拒绝,执行应该跳转到最近的拒绝处理程序。但是没有。所以错误被“卡住”了。没有代码来处理它。

在实践中,就像代码中的常规未处理错误一样,这意味着某些东西出现了严重的错误。

如果出现了常规错误,但是try..catch没有捕捉到,会发生什么情况?脚本在控制台中结束,并显示一条消息。类似的事情也会发生在未经处理的拒绝承诺上。

JavaScript引擎会跟踪这种拒绝并在这种情况下生成一个全局错误。如果运行上面的示例,就可以在控制台中看到它。

在浏览器中,我们可以使用unhandledrejection事件来捕获这样的错误:

window.addEventListener('unhandledrejection'function(event{
  // the event object has two special properties:
  alert(event.promise); // [object Promise] - the promise that generated the error
  alert(event.reason); // Error: Whoops! - the unhandled error object
});

new Promise(function({
  throw new Error("Whoops!");
}); // no catch to handle the error

事件是HTML标准的一部分。

如果发生了错误,但是没有.catch, unhandledrejection处理程序就会触发,并获取带有错误信息的事件对象,因此我们可以做一些事情。

通常这种错误是不可恢复的,所以我们最好的解决方法是通知用户这个问题,并可能向服务器报告这个事件。

在非浏览器环境中,如Node。还有其他方法可以跟踪未处理的错误。


浏览 38
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报