JS 异常处理

Error 对象

一旦代码解析或运行时发生错误,JavaScript引擎就会自动产生并抛出一个Error对象的实例,然后整个程序就中断在发生错误的地方。

Error 对象也可用于用户自定义的异常的基础对象。

Error 对象的实例有三个最基本的属性:

  • name:错误名称
  • message:错误提示信息
  • stack:错误的堆栈(非标准属性,但是大多数平台支持)

利用name和message这两个属性,可以对发生什么错误有一个大概的了解。

错误对象处理

异常的抛出方式

异常可以自动抛出或手动抛出。

一旦代码解析或运行时发生错误,JavaScript引擎就会自动产生并抛出一个Error对象的实例,然后整个程序就中断在发生错误的地方。

开发者也可以在预感程序无法继续执行的时候主动抛出错误。

抛出一个错误

当代码中发生错误时,我们通常会抛出一个 Error 对象。Error 对象可以作为扩展和创建自定义错误类型的原型。

抛出错误时,你必须使用 throw 关键字。为了捕获抛出的错误,则必须使用 try catch 语句把可能出错的代码块包起来,catch 的时候可以接收一个参数,该参数就是被抛出的错误。

在 try catch 语句之后有 finally,不论前面代码是否抛出错误 finally 里面的代码都会执行,这种语言的常见用途有:在 finally 中做些清理的工作。

ts 复制代码
try {
  throw new Error("Whoops!");
} catch (e) {
  console.error(e.name + ": " + e.message);
}

捕获异常

try catch 只能处理同步代码,异步产生的错误不能用 try catch 捕获,而要使用回调捕获。原因在于 try catch 的作用仅仅是捕获当前调用栈的错误。

错误类型

javascript规范中总共有8中错误类型构造函数

  • Error -- 错误对象
  • SyntaxError --解析过程语法错误
  • TypeError -- 不属于有效类型
  • ReferenceError -- 无效引用
  • RangeError -- 数值超出有效范围
  • URIError -- 解析URI编码出错
  • EvalError -- 调用eval函数错误
  • InternalError -- Javascript引擎内部错误的异常抛出, "递归太多"

处理特定错误

可以通过判断异常的类型来特定处理某一类的异常,即判断 constructor 属性,当使用现代 JavaScript 引擎时,可使用 instanceof 关键字

ts 复制代码
try {
  foo.bar();
} catch (e) {
  if (e instanceof EvalError) {
    console.error(e.name + ": " + e.message);
  } else if (e instanceof RangeError) {
    console.error(e.name + ": " + e.message);
  }
  // ... etc
  else {
    // If none of our cases matched leave the Error unhandled
    throw e;
  }
}

抛出一个非Error对象的值

同样需要注意的是,你可以抛出不是 Error 对象的任意值。如果恰巧你需要处理错误的调用栈信息和其他有意义的元数据,抛出一个非 Error 对象的错误没有任何帮助。

ts 复制代码
function runWithoutThrowing(func) {
    try {
        func();
    } catch (e) {
        console.log('There was an error, but I will not throw it.');
        console.log('The error message was: ', e, e.message)
    }
}

function funcThatThrowsString() {
    throw 'I am a String.';
}

runWithoutThrowing(funcThatThrowsString);

比如上面的代码,catch e 捕获到的值是一个字符串,没有message和stack等有用信息。

错误堆栈

调用栈

当有一个函数被调用的时候, 它就被压入到堆栈的顶部, 该函数运行完成之后, 又会从堆栈的顶部被移除。堆栈的数据结构就是后进先出。

些 JS 运行环境还提供了标准属性之外的属性,如 Node.js、Firefox、Chrome、Edge、IE 10、Opera 和 Safari 6+ 中会有 stack 属性,它包含了错误代码的调用栈,接下来我们简称错误堆栈。错误堆栈包含了产生该错误时完整的调用栈信息。

ts 复制代码
function c() {
  try {
    var bar = baz;
    throw new Error();
  } catch (e) {
    console.trace();
    console.error(e);
    console.log(e.stack);
  }
}

function b() {
  c();
}

function a() {
  b();
}
a();

比如上面的例子,通过打印e.stack,可以看到错误发生时的调用栈。

异常的传播

异常的传播是作用在函数调用栈。如果一个异常没有被catch,他会沿着调用栈层层传递,直到栈为空。当一个异常被 throw 出来的时候,传播就开始了。如果一个异常没有被 catch ,最终会打印在控制台中。错误类型是 UncaughtError。

使用promise进行错误处理

promise 链在错误(error)处理中十分强大。当一个 promise 被 reject 时,控制权将移交至最近的 rejection 处理程序。

比如下面的例子,.catch 不必是立即的。它可能在一个或多个 .then 之后出现。

链尾端的 .catch 的表现有点像 try..catch。我们可能有许多个 .then 处理程序,然后在尾端使用一个 .catch 处理上面的所有 error。

ts 复制代码
fetch("http://www.test.com")
  .then((response) => {
    console.log("接受到response");
    return response.json();
  })
  .catch((err) => console.log("错误", err));

隐式try...catch

promise 的执行者(executor)和 promise 的处理程序周围有一个"隐式的 try..catch"。如果发生异常,它就会被捕获,并被视为 rejection 进行处理。

ts 复制代码
new Promise((resolve, reject) => {
  throw new Error("执行错误"); // 执行处抛出错误
}).catch((err) => {
  console.log("捕获到错误", err);
});
ts 复制代码
new Promise((resolve, reject) => {
  resolve("正确执行");
})
  .then((res) => {
    throw new Error("发生错误", res);
  })
  .catch((err) => {
    console.log("捕获到错误", err);
  });

但对于异步抛出的错误,这里是无法捕获的。但通过reject返回的错误可以被捕获。

ts 复制代码
new Promise((resolve, reject) => {
  setTimeout(() => {
    throw new Error('发生错误')
  }, 1000)
}).catch((err) => {
  console.log("捕获到错误", err);
});
ts 复制代码
new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(new Error('发生错误'))
  }, 1000)
}).catch((err) => {
  console.log("捕获到错误", err);
});

catch 中处理错误

我们通常在catch中处理错误,catch语句会返回一个promise。

如果catch中处理了错误,那么会继续调用后续的执行程序。

如果catch中无法处理错误,可以将错误抛出,将控制权交给下一个最近的错误处理程序。

ts 复制代码
new Promise((resolve, reject) => {
  reject(new Error("发生错误"));
})
  .catch((err) => {
    if (err instanceof URIError) {
      // 处理已知错误
    } else {
      // 未知错误,再次抛出
      throw err;
    }
  })
  .then((res) => {
    console.log("then...", res);
  })
  .catch((e) => {
    console.log("捕获到错误", e);
  });

unhandledrejection

当 Promise 被 reject 且没有 reject 处理器的时候,会触发 unhandledrejection 事件。

换句话说:当 Promise 的状态变为 rejection 时,我们没有正确处理,让其一直冒泡(propagation),直至被进程捕获。这个 Promise 就被称为 unhandled promise rejection。

unhandledrejection 继承自 PromiseRejectionEvent,而 PromiseRejectionEvent 又继承自 Event。因此unhandledrejection 含有 PromiseRejectionEvent 和 Event 的属性和方法。主要包含两个有用的属性:

  • promise:特定的被 reject 且没有 reject 处理器的 Promise
  • reason:将会传入异常处理程序中的错误原因(如果存在)
ts 复制代码
new Promise((resolve, reject) => {
  reject(new Error("发生错误"));
})

window.addEventListener("unhandledrejection", (event) => {
console.warn('UNHANDLED PROMISE REJECTION:',  event.reason);
});

上面的例子中,触发了unhandledrejection 事件,未捕获的错误。

来源

Error

使用 promise 进行错误处理

JS基础系列之 ------ 💣 异常处理

相关推荐
木子020437 分钟前
前端VUE项目启动方式
前端·javascript·vue.js
endingCode1 小时前
45.坑王驾到第九期:Mac安装typescript后tsc命令无效的问题
javascript·macos·typescript
Myli_ing2 小时前
HTML的自动定义倒计时,这个配色存一下
前端·javascript·html
I_Am_Me_2 小时前
【JavaEE进阶】 JavaScript
开发语言·javascript·ecmascript
℘团子এ3 小时前
vue3中如何上传文件到腾讯云的桶(cosbrowser)
前端·javascript·腾讯云
学习前端的小z3 小时前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript
前端百草阁3 小时前
【TS简单上手,快速入门教程】————适合零基础
javascript·typescript
彭世瑜3 小时前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
Backstroke fish3 小时前
Token刷新机制
前端·javascript·vue.js·typescript·vue
zwjapple3 小时前
typescript里面正则的使用
开发语言·javascript·正则表达式