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基础系列之 ------ 💣 异常处理

相关推荐
托尼沙滩裤18 分钟前
【js面试题】js的数据结构
前端·javascript·数据结构
朝阳391 小时前
vue3【实战】来回拖拽放置图片
javascript·vue.js
不如喫茶去1 小时前
VUE自定义新增、复制、删除dom元素
前端·javascript·vue.js
阿垚啊1 小时前
vue事件参数
前端·javascript·vue.js
加仑小铁1 小时前
【区分vue2和vue3下的element UI Dialog 对话框组件,分别详细介绍属性,事件,方法如何使用,并举例】
javascript·vue.js·ui
Simaoya3 小时前
vue判断元素滚动到底部后加载更多
前端·javascript·vue.js
头顶一只喵喵3 小时前
Vue基础知识:Vue3.3出现的defineOptions,如何使用,解决了什么问题?
前端·javascript·vue.js·vue3
掘金安东尼3 小时前
上周前端发生哪些新鲜事儿?#370
前端·javascript·面试
黑色的糖果4 小时前
echarts横向立体3D柱状图
前端·javascript·echarts
茶卡盐佑星_4 小时前
vue3.0所采用的composition Api与vue2.x使用的Option Api有什么区别
前端·javascript·vue.js