Vue中如何优雅的捕获 Promise 异常详解
作者:WujieLi 发布时间:2023-07-02 16:56:14
常规的异常捕获方式
在 Promise 提供了一个 .catch
方法用来捕获异常,假设有很多异步请求,通常会把 .catch
方法放在链式调用的最末尾。正常情况下末尾的 .catch
不会被触发,但当前面的任何一个 Promise rejected 之后,.catch
就可以捕获到异常
promiseFunction1({
year: unref(year),
})
.then((res) => {
if (res.status === 200) {
return promiseFunction2(res.data?.name || "");
}
})
.then((res) => {
if (res.status === 200) {
const getUserInfo = userInfoResult.data;
// ... 具体操作
}
})
// 异常捕获
.catch((error) => console.error(error));
如果使用 Promise 的语法糖 async / await 的话,可以使用更符合直觉的 try...catch
捕获异常,上面这个请求例子就可以修改为
async function handleUserInfo() {
try {
const userResult = await promiseFunction1({ year: unref(year) });
if (userResult.status !== 200) return;
const userInfoResult = await promiseFunction2(res.data?.name || "");
if (userInfoResult.status !== 200) return;
getUserInfo = userInfoResult.data;
// ... 具体操作
} catch (error) {
console.error(error);
}
}
不管是 .then
方法还是 try...catch
都需要增加一些代码操作,最重要的是可能会忘记捕获异常,所以下面介绍两个更好一些的解决方案
好一些的方式:await-to-js
await-to-js 是一个大佬对 async / await 返回内容进行的一层封装,在不用 try...catch
的方式下也能进行异常捕获
在使用前需要先引入这个依赖:npm i await-to-js
,下面我们来改写简化一下之前的异常捕获方式
import to from 'await-to-js';
async function handleUserInfo() {
const [userError, userResult] = await promiseFunction1({ year: unref(year) })
if (userResult.status !== 200) return
const [userInfoError, userInfoResult] = await promiseFunction2(res.data?.name || "")
if (userInfoResult.status !== 200) return
getUserInfo = userInfoResult.data
// ... 具体操作
}
await-to-js 的实现也就短短几行代码,本质就是对 Promise 的 .then
和 .catch
返回结果进行组合,然后整体作为一个 Promise 再返回出去
export function to<T, U = Error> (
promise: Promise<T>,
errorExt?: object // 传递给 err 对象的附加信息
): Promise<[U, undefined] | [null, T]> {
return promise
.then<[null, T]>((data: T) => [null, data])
.catch<[U, undefined]>((err: U) => {
if (errorExt) {
const parsedError = Object.assign({}, err, errorExt);
return [parsedError, undefined];
}
return [err, undefined];
});
}
export default to;
虽然 await-to-js 简化了代码,但还是需要引入依赖,对请求进行一层包裹,还是稍微麻烦了一点,如果我们对异常处理没有特殊处理的需要,仅仅只是捕获并且抛出,为了追求更简洁的代码;
或者项目中有非常多的地方没有异常捕获,需要一个一个的手工增加非常麻烦,针对这两种情况,还有没有更好的办法呢?
更好的方式:全局捕获
在 Vue2 的全局配置中提供了一个 errorHandler
钩子可以用于捕获全局异常,但是最低版本要求 2.2.0+
errorHandler
第一个参数 err
是具体的错误信息,第二个参数 vm
是 Vue 组件信息,第三个参数 info
是 Vue 特定的错误信息,比如错误所在的生命周期钩子。一般为了捕获 Vue 特定的 info
信息,在内部处理时还会加上一层 nextTick
,确保捕获的是 DOM 渲染完成之后的信息。另外最好在根据不同环境配置判断是否需要捕获异常,增加程序的灵活性
// errorHandler 使用示例
import Vue from 'vue'
// 配置项形式:'development' | ['development', 'production']
const { errorLog: needErrorLog } = settings
// 根据配置判断什么环境下需要捕获异常
function checkNeedErrorLog() {
const env = process.env.NODE_ENV
if (isString(needErrorLog)) {
return env === needErrorLog
}
if (isArray(needErrorLog)) {
return needErrorLog.includes(env)
}
return false
}
// 全局异常捕获
if (checkNeedErrorLog()) {
Vue.config.errorHandler = function (err, vm, info) {
Vue.nextTick(() => {
console.error(`[${projectName}]: ${err}。`, `Vue info: ${info}`, vm)
})
}
}
根据官网的描述,不同的 Vue 版本捕获的信息不同,所以建议最好是更新 Vue 2.6.0 以上的版本,这样就可以全局捕获到 Promise 和 async / await 抛出的异常了,
从 2.2.0 起,errorHandler
钩子也会捕获组件生命周期钩子里的错误。同样的,当这个钩子是 undefined
时,被捕获的错误会通过 console.error
输出而避免应用崩溃
从 2.4.0 起,errorHandler
钩子也会捕获 Vue 自定义事件处理函数内部的错误
从 2.6.0 起,errorHandler
钩子也会捕获 v-on
DOM * 内部抛出的错误。另外,如果任何被覆盖的钩子或处理函数返回一个 Promise 链 (例如 async 函数),则来自其 Promise 链的错误也会被处理
在 Vue3 中,除了提供 errorHandler
钩子外,还提供了 warnHandler
钩子,两个钩子的用法相同,区别是是 warnHandler
只在开发环境生效,生产环境会被忽略
app.config.warnHandler = function(msg, vm, trace) {
// `trace` 是组件的继承关系追踪
}
来源:https://juejin.cn/post/7128992038980157471


猜你喜欢
- Python中的缩进(Indentation)决定了代码的作用域范围。这一点和传统的c/c++有很大的不同(传统的c/c++使用花括号{}符
- 方法1: 代码如下:truncate table TableName 删除表中的所有的数据的同时,将自动增长清零。 如果有外键参考这个表,这
- 前言Scrapy是一个开源的网络爬虫框架,Python编写的。最初设计用于网页抓取,也可以用来提取数据使用API或作为一个通用的网络爬虫。是
- Deepin安装开发环境 系统环境配置挂载分区1、挂载分区(需要先卸载分区):deepin:sudo mount -t ext4 /dev/
- 这回我们看看如何实现判断两个对像的内容是否相等。这里有一个克隆结果原则是针对Java语言的,当然JavaScript也可以胜任。克隆满足的条
- python的子进程嘛,就是利用python打开一个子进程(当然像是一句废话),但是可能和我们理解的不太一样。一:如何理解?我们可能的理解:
- str字符串s = '中文' # s: <type 'str'>s是个str对象,中
- 查看表空间的名称及大小代码如下:SQL>select t.tablespace_name, round(sum(bytes/(1024
- 1. 下载darknet源码在命令窗口(terminal)中进入你想存放darknet源码的路径,然后在该路径下输入依次输入以下命令:git
- 方法一:def commaSpiltList(self, listData): listData = list(listData) strs
- dbutils封装文件传送门DBUtils是一套Python数据库连接池包,并允许对非线程安全的数据库接口进行线程安全包装。DBUtils来
- 调用JSON.stringify将对象转为对应的字符串时,如果包含时间对象,时间对象会被转换为国家标准时间(ISO),而不是当前国家区域的时
- 前言还记得这个九九乘法表吗,这次课后相信你可以用代码给你的小弟弟妹妹们变出这份“葵花宝典”。循环如果要把循环翻译成机器语言,那他对应的可以是
- 数据库复制:简单来说,数据库复制就是由两台服务器,主服务器和备份服务器,主服务器修改后,备份服务器自动修改。复制的模式有两种:推送模式和请求
- 判断字段是否存在:DROP PROCEDURE IF EXISTS schema_change; DELIMITER //CREATE PR
- 在命令行中使用 Python 时,它可以接收大约 20 个选项(option),语法格式如下:python [-bBdEhiIOqsSuvV
- pandas loc的指定条件索引(布尔索引)pandas中的loc不仅仅可以用于直接的标签的索引,也可以用于指定条件的索引。1.准备数据首
- 前言对于JavaScript程序的调试,相比于alert(),使用console.log()是一种更好的方式,原因在于:alert()函数会
- 一、数字类型。数字类型按照我的分类方法分为三类:整数类、小数类和数字类。 我所谓的“数字类”,就是指DECIMAL和NUMERIC,它们是同
- 主键与外键的关系,通俗点儿讲,我现在有一个论坛,有两张表,一张是主贴 thread,一张是回帖 reply先说说主键,主键是表里面唯一识别记