(JavaScript)使用promise进行错误处理、再次抛出、未处理的rejection、Fetch错误处理示例

  • 当一个promise被reject时,控制权将移交至最近的rejection处理程序
//下面代码中所fetch的URL是错的,.catch对这个error进行了处理
fetch('https://no-such-server.blabla') // rejects
  .then(response => response.json())
  .catch(err => alert(err)) // TypeError: failed to fetch(这里的文字可能有所不同)
  • 如果想要捕获所有的error,将.catch附加到链的末尾
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 根本不会被触发。但是如果上述任意一个 promisereject(网络问题或者无效的 json 或其他),.catch 就会捕获它。

隐式try…catch

  • Promise 的执行者(executor)和 promise 的处理程序(handler)周围有一个“隐式的 try…catch”。如果发生异常,它就会被捕获,并被视为 rejection 进行处理。
  • executor:是在new Promise里面的代码
  • handler:是在.catch、.then、.finally里面的代码

发生在Promise中:

下面这两段代码在工作上完全相同

new Promise((resolve, reject) => {
  throw new Error("Whoops!");
}).catch(alert); // Error: Whoops!
new Promise((resolve, reject) => {
  reject(new Error("Whoops!"));
}).catch(alert); // Error: Whoops!
  • 在 executor 周围的“隐式 try…catch”自动捕获了 error,并将其变为 rejected promise。
  • 是所有同步代码都会得到处理,如果错误是异步稍后生成的。promise无法处理
//.catch无法触发
new Promise(function(resolve, reject) {
  setTimeout(() => {
    throw new Error("Whoops!");
  }, 1000);
}).catch(alert);

发生在handler中
  • throw error:
new Promise((resolve, reject) => {
  resolve("ok");
}).then((result) => {
  throw new Error("Whoops!"); // reject 这个 promise
}).catch(alert); // Error: Whoops!
  • 编程 error:
new Promise((resolve, reject) => {
  resolve("ok");
}).then((result) => {
  blabla(); // 没有这个函数
}).catch(alert); // ReferenceError: blabla is not defined

最后的 .catch 不仅会捕获显式的 rejection,还会捕获它上面的处理程序(handler)中意外出现的 error


再次抛出(Rethrowing)

  • 如果在.catch中throw,那么控制权就会被移交给下一个最近的error处理程序(handler)
  • 如果我们处理该 error 并正常完成,那么它将继续到最近的成功的 .then 处理程序(handler)。

.catch 成功处理了 error:
// 执行流:catch -> then
new Promise((resolve, reject) => {

  throw new Error("Whoops!");
  
}).catch(function(error) {

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

//.catch 块正常完成。所以下一个成功的 .then 处理程序(handler)就会被调用。
}).then(() => alert("Next successful handler runs"));

handler捕获了error,无法处理,将其再次抛出
// 执行流:catch -> catch
new Promise((resolve, reject) => {

  throw new Error("Whoops!");

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

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

    throw error; // 再次抛出此 error 或另外一个 error,执行将跳转至下一个 catch
  }

}).then(function() {
  /* 不在这里运行 */
}).catch(error => { // (**)

  alert(`The unknown error has occurred: ${error}`);
  // 不会返回任何内容 => 执行正常进行

});
  • 执行从第一个 .catch (*) 沿着链跳转至下一个 (**)。

未处理的rejection

  • 当一个error没有被处理会发生什么?
    Javascript引擎会跟踪此类rejection,在这种情况下会生成一个全局的error
  • 在浏览器中,我们可以使用 unhandledrejection 事件来捕获这类 error
window.addEventListener('unhandledrejection', function(event) {
  // 这个事件对象有两个特殊的属性:
  alert(event.promise); // [object Promise] - 生成该全局 error 的 promise
  alert(event.reason); // Error: Whoops! - 未处理的 error 对象
});

new Promise(function() {
  throw new Error("Whoops!");
}); // 没有用来处理 error 的 catch
  • 在任何情况下我们都应该有 unhandledrejection 事件处理程序(用于浏览器,以及其他环境的模拟),以跟踪未处理的 error 并告知用户(可能还有我们的服务器)有关信息,以使我们的应用程序永远不会“死掉”。

Fetch 错误处理示例

  • 如果远程服务器返回相应错误404,甚至是错误500,这些都会被认为是合法的响应
  • 所以我们多添加一步:我们应该检查具有 HTTP 状态的 response.status 属性,如果不是 200 就抛出错误。
class HttpError extends Error { // (1)
  constructor(response) {
    super(`${response.status} for ${response.url}`);
    this.name = 'HttpError';
    this.response = response;
  }
}

function loadJson(url) { // (2)
  return fetch(url)
    .then(response => {
      if (response.status == 200) {
        return response.json();
      } else {
        throw new HttpError(response);
      }
    })
}

loadJson('no-such-user.json') // (3)
  .catch(alert); // HttpError: 404 for .../no-such-user.json
  • 我们可以使用 instanceof 很容易地在错误处理代码中检查错误。
    下面的代码从 GitHub 加载给定名称的用户。如果没有这个用户,它将告知用户填写正确的名称:
function demoGithubUser() {
  let name = prompt("Enter a name?", "iliakan");

  return loadJson(`https://api.github.com/users/${name}`)
    .then(user => {
      alert(`Full name: ${user.name}.`);
      return user;
    })
    .catch(err => {
      if (err instanceof HttpError && err.response.status == 404) {
        alert("No such user, please reenter.");
        return demoGithubUser();
      } else {
        throw err; // (*)
      }
    });
}

demoGithubUser();

其它
  • 如果我们有加载指示(load-indication),.finally 是一个很好的处理程序(handler),在 fetch 完成时停止它:
function demoGithubUser() {
  let name = prompt("Enter a name?", "iliakan");

  document.body.style.opacity = 0.3; // (1) 开始指示(indication)

  return loadJson(`https://api.github.com/users/${name}`)
    .finally(() => { // (2) 停止指示(indication)
      document.body.style.opacity = '';
      return new Promise(resolve => setTimeout(resolve)); // (*)
    })
    .then(user => {
      alert(`Full name: ${user.name}.`);
      return user;
    })
    .catch(err => {
      if (err instanceof HttpError && err.response.status == 404) {
        alert("No such user, please reenter.");
        return demoGithubUser();
      } else {
        throw err;
      }
    });
}

demoGithubUser();
  • 此处的 (1) 行,我们通过调暗文档来指示加载。指示方法没有什么问题,可以使用任何类型的指示来代替。
  • 当 promise 得以解决,fetch 可以是成功或者错误,finally 在 (2) 行触发并终止加载指示。
  • 有一个浏览器技巧,(*) 是从 finally 返回零延时(zero-timeout)的 promise。这是因为一些浏览器(比如 Chrome)需要“一点时间”外的 promise 处理程序来绘制文档的更改。因此它确保在进入链下一步之前,指示在视觉上是停止的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值