js异常处理方案

文章目录

  • 异常处理方案
  • 同步代码的异常处理
  • Promise 的异常处理
  • async await 的异常处理

感谢阅读,觉得有帮助可以点点关注点点赞,谢谢!

异常处理方案

在JS开发中,处理异常包括两步:先抛出异常,然后捕获异常。
为什么要做异常处理
异常处理非常重要,至少有以下几个原因:

1.防止程序报错甚至停止运行:当代码执行过程中发生错误或异常时,如果没有适当的异常处理机制,程序可能会报错、停止运行,甚至崩溃。通过处理异常,我们可以捕获错误并采取适当的措施避免系统报错。
2.错误排查和调试:异常处理有助于定位和排查错误。可以通过捕获异常并输出相关信息,比如打印日志、错误上报、跟踪堆栈等等,以便快速定位问题所在,并进行调试和修复。
3.提高代码健壮性和可靠性:可以采取适当的措施处理潜在的异常情况,从而减少程序出错的可能性。
4.提升用户体验:通过兜底、容错、容灾等异常处理方案,可以向用户提供有效的错误信息提示,而不是让用户界面无响应甚至白屏。

抛出异常
抛出异常的使用场景举例:
我们经常会封装一些工具函数,这些函数可能给自己用,也可能给外部团队用。
在函数内部,如果不符合预期的业务逻辑,或者遇到异常情况时,很多人的写法是直接 return,不往下执行了。但是 return 的写法存在一个很大的弊端:调用者不知道是因为函数内部没有正常执行,还是执行的返回结果就是一个undefined。return 的写法只是规避了问题,没有解决问题。建议的做法是:我们需要手动抛出异常

捕获异常
如果只是抛出异常,而不捕获异常的话,是比较危险的。这意味着当前任务立即终止,不再执行(当然,后续的其他任务会正常执行)。此外,这个异常信息会层层往上,抛给上层的调用者。如果一直未被捕获,则最终会抛给浏览器,浏览器控制台就会报错。

接下来,我们看一下不同代码场景下的异常处理方案。

上报异常
如果有必要的话,你可以把异常信息和日志,上报给监控服务器,然后集中分析。我每天上班第一件事,就是打开监控系统,看错误日志,然后对症下药解决问题

同步代码的异常处理

通过 throw 抛出异常
我们可以通过 throw关键字,抛出一个用户自定义的异常。当代码执行时遇到 throw 语句时,当前函数会停止停止,即:当前函数 throw 后面的代码不会再执行。

throw 意思是,告诉调用者,当前被调用的函数报错了,调用者接下来需要捕获异常或者修改代码逻辑。

可以在 throw 的后面添加表达式或者数据类型,将添加的内容抛出去。数据类型可以是:number、string、boolean、对象等。

代码举例:

function sum(num1, num2) {
  if (typeof num1 !== "number") {
    throw "type error: num1传入的类型有问题, 必须是number类型"
  }

  if (typeof num2 !== "number") {
    throw "type error: num2传入的类型有问题, 必须是number类型"
  }

  return num1 + num2
}

sum('a', 'b');

打印结果:
在这里插入图片描述
当然,我们还可以 throw一个封装好的对象。比如:

class myError {
  constructor(errCode, errMsg) {
    this.errCode = errMsg;
    this.errMsg = errMsg;
  }
}

function foo() {
  throw new myError(-1, 'not login');
}

foo();

上面这种写法比较麻烦,一般不这么写。其实,JS中已经内置了 Error 类,专门用于生成错误信息。
Error 类
JS内置的 Error 类非常好用。

代码举例:

function foo() {
  throw new Error('not login');
}


foo();

打印结果:
在这里插入图片描述
上面的打印结果可以看到,通过 Error 抛出来的错误,不仅可以看到报错信息,还可以看到调用栈,便于快速定位问题所在。非常方便。

通过 try catch 捕获异常
同步代码,只抛出异常,不捕获异常的代码举例:

function foo() {
  throw new Error('not login');
}

foo();
// 当前任务立即终止,不再执行;下面这行代码和 foo() 都在同一个 同步任务 中
console.log('qianguyihao');

打印结果:
在这里插入图片描述
可以看到,最后一行的 log 并没有执行。

我们可以使用 try catch 抛出异常, 对上述代码进行改进。代码举例:

function foo() {
  throw new Error('not login');
}

// 通过 try catch 手动捕获异常
try {
  foo();
} catch (err) {
  console.log(err);
}

// 当前任务的后续代码会继续执行
console.log('qianguyihao');

打印结果:
在这里插入图片描述
通过 try catch finally 捕获异常
如果有些代码必须要执行,我们可以放到 finally 里。

不管是否遇到异常,finally的代码一定会执行。
如果 try 和 finally 中都有返回值,那么会使用finally中的返回值。

代码举例:

function foo() {
  throw new Error('not login');
}

// 通过 try catch 捕获异常
try {
  foo();
} catch (err) {
  console.log(err);
} finally {
  console.log("finally")
}

// 后续代码会继续执行
console.log('qianguyihao');

try catch 只能捕获同步代码的异常
try catch只能捕获同步代码里的异常,而 Promise.reject() 是异步代码。

原因是:当异步函数抛出异常时,对于宏任务而言,执行函数时已经将该函数推入栈,此时并不在 try-catch 所在的栈,所以 try-catch 并不能捕获到错误。对于微任务而言(比如 promise)promise 的构造函数的异常只能被自带的 reject 也就是.catch 函数捕获到。

使用 window.onerror 监听未被捕获的代码异常
如果JS代码抛出了异常但没有进行捕获,我们可以使用 JS 自带的 window.onerror 事件监听到这些错误。
代码举例:

// 监听同步代码的异常
window.onerror = (event) => {
  console.error('onerror 监听到未被捕获的异常:', event)
};

function foo1() {
  throw new Error('not login');
}

function foo2() {
  throw new Error('network error');
}

foo1();
foo2();

打印结果:
在这里插入图片描述

Promise 的异常处理

reject() 会自动抛出异常
在使用 Promise 时,当我们调用了 reject() 之后,系统会自动抛出异常,不需要我们手动抛出异常。这是 Promise的内部机制。但是我们需要手动捕获异常。

当 Promise 进入 rejected状态之后,会触发 catch()方法的执行,捕获异常。此时,成功完成了Promise异常处理的闭环。

在then() 中抛出异常(重要)
当then()方法传入的回调函数中,如果遇到异常或者手动抛出异常,那么,then()所返回的新的 Promise 会进入rejected 状态,进而触发新Promise 的 catch() 方法的执行,做异常捕获。

场景1:在then()方法传入的回调函数中,如果代码在执行时遇到异常,系统会自动抛出异常。此时我们需要在 catch() 里手动捕获异常,否则会报错。

自动抛出异常的代码举例:(由于没有捕获异常,所以会报错)

const myPromise = new Promise((resolve, reject) => {
  resolve('qianguyihao1 fulfilled');
});

myPromise.then(res => {
  console.log('res1:', res);
  // 显然,person 并没有 forEach()方法。所以,代码在执行时,会遇到异常。
  const person = { name: 'vae' };
  person.forEach(item => {
    console.log('item:', item);
  })
  // 这行代码不会执行,因为上面的代码报错了
  console.log('qianguyihao2');
}).then(res => {
  console.log('res2:', res);
})

// 定时器里的代码正常执行
setTimeout(() => {
  console.log('qianguyihao3');
}, 100)

运行结果:
在这里插入图片描述
代码改进:(代码在执行时遇到异常,此时我们捕获异常,所以系统不会报错,这才是推荐的写法)

const myPromise = new Promise((resolve, reject) => {
  resolve('qianguyihao1 fulfilled');
});

myPromise.then(res => {
  console.log('res1:', res);
  // 显然,person 并没有 forEach()方法。所以,代码在执行时,会遇到异常。
  const person = { name: 'vae' };
  person.forEach(item => {
    console.log('item:', item);
  })
  // 这行代码不会执行,因为上面的代码报错了
  console.log('qianguyihao2');
}).then(res => {
  console.log('res2:', res);
}).catch(err => {
  // 在 catch()方法传入的会调函数里,捕获异常
  console.log('err2:', err);
})

// 定时器里的代码正常执行
setTimeout(() => {
  console.log('qianguyihao3');
}, 100)

打印结果
在这里插入图片描述
场景2:在then()方法传入的回调函数中,如果我们手动抛出异常,此时我们需要在 catch() 里手动捕获异常,否则会报错。

代码举例:(手动抛出异常,未捕获,所以会报错)

const myPromise = new Promise((resolve, reject) => {
  resolve('qianguyihao fulfilled 1');
});

myPromise.then(res => {
  console.log('res1:', res);
  // 手动抛出异常
  throw new Error('qianguyihao rejected 2')
}).then(res => {
  console.log('res2:', res);
})

// 定时器里的代码正常执行
setTimeout(() => {
  console.log('qianguyihao3');
}, 100)

打印结果:
在这里插入图片描述
代码改进:(代码在执行时遇到异常,此时我们捕获异常,所以系统不会报错,这才是推荐的写法)

const myPromise = new Promise((resolve, reject) => {
  resolve('qianguyihao fulfilled 1');
});

myPromise.then(res => {
  console.log('res1:', res);
  // 手动抛出异常
  throw new Error('qianguyihao rejected 2')
}).then(res => {
  console.log('res2:', res);
}, (err) => {
  console.log('err2:', err);
})

// 定时器里的代码正常执行
setTimeout(() => {
  console.log('qianguyihao3');
}, 100)

打印结果:
在这里插入图片描述
使用 unhandledrejection 事件监听未被捕获的Promise异常
如果Promise抛出了异常但没有进行捕获,我们可以使用JS自带的 unhandledrejection 事件监听到这些错误。这个事件非常有用,尤其是当我们需要集中做日志收集时,屡试不爽。这个事件只能用于监听 Promise 中的异常,不能用于其他同步代码的异常。

先来看下面这行代码:

const myPromise = new Promise((resolve, reject) => {
  console.log('qianguyihao1');
  reject('not login');
  console.log('qianguyihao2');
})

打印结果:
在这里插入图片描述
上面的代码抛出了异常,但没有捕获异常,所以我们可以用 unhandledrejection 事件监听到。代码举例:

// 监听未被捕获的 Promise 异常
window.addEventListener('unhandledrejection', (event) => {
  console.error(`unhandledrejection 监听到异常,写法1: ${event.reason}`)
});

window.onunhandledrejection = event => {
  console.error(`unhandledrejection 监听到异常,写法2: ${event.reason}`);
};

window.onerror = (event) => {
  console.error('onerror 监听到异常:', event);
};


const promise1 = new Promise((resolve, reject) => {
  reject('not login');
})

const promise2 = new Promise((resolve, reject) => {
  throw new Error('network error');
  resolve();
})

打印结果:
在这里插入图片描述
可以看到,promise1 和 Promise2 的异常,都被 unhandledrejection 事件收集到了。

代码举例2:

window.addEventListener('unhandledrejection', (event) => {
  console.error(`unhandledrejection 监听到异常: ${event.reason}`)
});

window.onerror = (event) => {
  console.error('onerror 监听到异常:', event);
};

const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    throw new Error('not login');
    resolve();
  }, 100);
})

打印结果:
在这里插入图片描述
上面的代码中,unhandledrejection 无法监听异常,因为定时器里的代码属于宏任务。

resolve()之后,再报错无效
代码举例:

const myPromise = new Promise((resolve, reject) => {
  resolve('fulfilled');
  throw new Error("自定义错误");
});

myPromise.then(res => {
  console.log("res", res);
  return res + 1;
}).catch(err => {
  console.log("err:", err);
});

打印结果:

res fulfilled

上方代码中,第3行的异常代码相当于没写。因为 resolve()之后,Promise的状态会立即进入 fulfilled,然后走到 then(),状态不可逆。

async await 的异常处理

捕获异常
代码举例:

function requestData1() {
  return new Promise((resolve, reject) => {
    reject('任务1失败');
  })
}

function requestData2() {
  return new Promise((resolve, reject) => {
    resolve('任务2成功');
  })
}

async function getData() {
  // requestData1 在执行时,遇到异常
  await requestData1();
  /*
  由于上面的代码在执行是遇到异常,所以,这里虽然什么都没写,底层默认写了如下代码:
  return Promise.reject('任务1失败');
  */

  // 下面这两行代码不会再执行了,因为上面的代码遇到了异常
  console.log('qianguyihao1');
  await requestData2();
}

getData();

// 【注意】定时器里的代码会正常实行,因为它在另外一个宏任务里,不在上面的微任务里
setTimeout(() => {
  console.log('qianguyihao2');
}, 100)

打印结果:
在这里插入图片描述
所以,为了避免上述问题,我们还需要手动捕获异常。我们捕获到异常之后,这个异常就不会继续网上抛了,更不会抛给浏览器。

感谢阅读,觉得有帮助可以点点关注点点赞,谢谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/751631.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

一站式uniapp优质源码项目模版交易平台的崛起与影响

一、引言 随着信息技术的飞速发展,软件源码已成为推动行业进步的重要力量。源码的获取、交易和流通,对于开发者、企业以及项目团队而言,具有极其重要的意义。为满足市场对高质量源码资源的迫切需求,一站式uniapp优质源码项目模版…

深度学习实验第T1周:实现mnist手写数字识别

>- **🍨 本文为[🔗365天深度学习训练营](https://mp.weixin.qq.com/s/0dvHCaOoFnW8SCp3JpzKxg) 中的学习记录博客** >- **🍖 原作者:[K同学啊](https://mtyjkh.blog.csdn.net/)** 目录 目录 一、前言 二、我的环境 三、…

【数据集划分——针对于原先图片已经整理好类别】训练集|验证集|测试集

目标:用split-folders进行数据集划分 学习资源:https://www.youtube.com/watch?vC6wbr1jJvVs 努力的小巴掌 记录计算机视觉学习道路上的所思所得。 现在已经有了数据集,并且,注意,是已经划分好类别的! …

esp12实现的网络时钟校准

网络时间的获取是通过向第三方服务器发送GET请求获取并解析出来的。 在本篇博客中,网络时间的获取是一种自动的行为,当系统成功连接WiFi获取到网络天气后,系统将自动获取并解析得到时间和日期,为了减少误差每两分钟左右进行一次校…

【Docker】创建 swarm 集群

目录 1. 更改防火墙设置 2. 安装 Docker 组件 3. 启动 Docker 服务,并检查服务状态。 4. 修改配置文件,监听同一端口号。 5. 下载 Swarm 组件 6. 创建集群,加入节点 7. 启动集群 8. 查询集群节点信息 9. 查询集群具体信息 10. 查询…

用Python设置Excel工作表网格线的隐藏与显示

Excel表格界面的直观性很大程度上得益于表格中的网格线设计,这些线条帮助用户精确对齐数据,清晰划分单元格。网格线是Excel界面中默认显示的辅助线,用于辅助定位,与单元格边框不同,不影响打印输出。然而,在…

【Linux:文件描述符】

文件描述符: 文件描述符的分配原则:最小未分配原则 每一个进程中有一个task_struct结构体(PCB),而task_struct中含有struct file_sturct*file的指针,该指针指向了一个struct files_struct的结构体该结构体中含有一个f…

Python27 神经网络中的重要概念和可视化实现

1. 神经网络背后的直观知识 神经网络的工作方式非常相似:它接受多个输入,经过多个隐藏层中的多个神经元进行处理,并通过输出层返回结果,这个过程在技术上称为“前向传播”。 接下来,将神经网络的输出与实际输出进行比…

Java | Leetcode Java题解之第202题快乐数

题目&#xff1a; 题解&#xff1a; class Solution {private static Set<Integer> cycleMembers new HashSet<>(Arrays.asList(4, 16, 37, 58, 89, 145, 42, 20));public int getNext(int n) {int totalSum 0;while (n > 0) {int d n % 10;n n / 10;totalS…

Windows下activemq开启jmx

1.activemq版本信息 activemq&#xff1a;apache-activemq-5.18.4 2.Windows下activemq开启jmx 1.进入activemq conf目录&#xff0c;备份activemq.xml文件 2.编辑activemq.xml文件&#xff0c;在broker节点增加useJmx"true" <broker xmlns"http://active…

Vuetify3:​快捷回到顶部

在Vuetify 3中&#xff0c;要实现回到顶部&#xff0c;我们需要创建悬浮按钮&#xff0c;如下&#xff1a; <template><v-list><div class"position-fixed right-0 bottom-0" style"top:50%;"><v-list-item ><v-btn icon"…

黑马点评项目总结1-使用Session发送验证码和登录login和 使用Redis存储验证码和Redis的token登录

黑马先是总结了从session实现登录&#xff0c;然后是因为如果使用了集群方式的服务器的话&#xff0c;存在集群共享session互相拷贝效率低下的问题&#xff0c;接着引出了速度更快的内存型的kv数据库Redis&#xff0c; 使用Session发送验证码和登录login 举个例子&#xff1a…

『Django』模型入门教程-操作MySQL

theme: smartblue 点赞 关注 收藏 学会了 本文简介 一个后台如果没有数据库可以说废了一半。日常开发中大多数时候都在与数据库打交道。Django 为我们提供了一种更简单的操作数据库的方式。 在 Django 中&#xff0c;模型(Model)是用来定义数据库结构的类。每个模型类通常对…

kali下安装使用蚁剑(AntSword)

目录 0x00 介绍0x01 安装0x02 使用1. 设置代理2. 请求头配置3. 编码器 0x00 介绍 蚁剑&#xff08;AntSword&#xff09;是一个webshell管理工具。 官方文档&#xff1a;https://www.yuque.com/antswordproject/antsword 0x01 安装 在kali中安装蚁剑&#xff0c;分为两部分&am…

matlab绘制二维曲线,如何设置线型、颜色、标记点类型、如何设置坐标轴、matlab 图表标注、在图中标记想要的点

matlab绘制二维曲线&#xff0c;如何设置线型、颜色、标记点类型、如何设置坐标轴、matlab 图表如何标注、如何在图中标记想要的点 matlab绘制二维曲线&#xff0c;如何在图中标记想要的点。。。如何设置线型、颜色、标记点类型。。。如何设置坐标轴。。。matlab 图表标注操作…

视频网站系统

摘 要 随着互联网的快速发展和人们对视频内容的需求增加&#xff0c;视频网站成为了人们获取信息和娱乐的重要平台。本论文基于SpringBoot框架&#xff0c;设计与实现了一个视频网站系统。首先&#xff0c;通过对国内外视频网站发展现状的调研&#xff0c;分析了视频网站的背景…

潮玩手办盲盒前端项目模版的技术探索与应用案例

一、引言 在数字化时代&#xff0c;随着消费者对个性化和艺术化产品的需求日益增长&#xff0c;潮玩手办和盲盒市场逐渐崭露头角。为了满足这一市场需求&#xff0c;前端技术团队需要构建一个功能丰富、用户友好的在线平台。本文旨在探讨潮玩手办盲盒前端项目模版的技术实现&a…

C++ | Leetcode C++题解之第201题数字范围按位与

题目&#xff1a; 题解&#xff1a; class Solution { public:int rangeBitwiseAnd(int m, int n) {while (m < n) {// 抹去最右边的 1n n & (n - 1);}return n;} };

序列检测器(Moore型)

目录 描述 输入描述&#xff1a; 输出描述&#xff1a; 参考代码 描述 请用Moore型状态机实现序列“1101”从左至右的不重叠检测。 电路的接口如下图所示。当检测到“1101”&#xff0c;Y输出一个时钟周期的高电平脉冲。 接口电路图如下&#xff1a; 输入描述&#xff1a…