抛出错误_不用try catch,如何机智的捕获错误

3b173c65022d4926185103db53e130b5.png

这是多个feature组合使用后实现的神奇效果,在React源码中被广泛使用。

当我读源码看到这里时,心情经历了:

懵逼 -- 困惑 -- 沉思 -- 查文档 -- 豁然开朗

看完此文,相信你也会发出感叹:

还能这么玩?

起源

我们知道,React中有个特性Error Boundary,帮助我们在组件发生错误时显示“错误状态”的UI。

为了实现这个特性,就一定需要捕获到错误。

所以在React源码中,所有用户代码都被包裹在一个方法中执行。

类似如下:

function wrapper(func) {try {func();} catch(e) {// ...处理错误}
}

比如触发componentDidMount时:

wrapper(componentDidMount);

本来一切都很完美,但是React作为世界级前端框架,受众广泛,凡事都讲究做到极致。

这不,有人提issue:

你们这样在try catch中执行用户代码会让浏览器调试工具的Pause on exceptions失效。

Pause on exceptions失效的来龙去脉

Pause on exceptions是什么?

他是浏览器调试工具source面板的一个功能。

902d447fc0901f9a4477bf624d32506d.png

开启该功能后,在运行时遇到会抛出错误的代码,代码的执行会自动停在该行,就像在该行打了断点一样。

比如,执行如下代码,并开启该功能:

let a = c;

代码的执行会在该行暂停。

24ff75231d758d083645c9685d8ad75a.png

这个功能可以很方便的帮我们发现未捕获的错误发生的位置。

但是,当React用户代码包裹在try catch后,即使代码抛出错误,也会被catch

Pause on exceptions无法在抛出错误的用户代码处暂停,因为error已经被React catch了。

除非我们进一步开启Pause on caught exceptions

5129d0871114f5410c3f71ea6f9ecd66.png

开启该功能,使代码在捕获的错误发生的位置暂停。

如何解决

对用户来说,我写在componentDidMount中的代码明明未捕获错误,可是错误发生时Pause on exceptions却失效了,确实有些让人困惑。

所以,在生产环境,React继续使用try catch实现wrapper

而在开发环境,为了更好的调试体验,需要重新实现一套try catch机制,包含如下功能:

  • 捕获用户代码抛出的错误,使Error Boundary功能正常运行
  • 不捕获用户代码抛出的错误,使Pause on exceptions不失效

这看似矛盾的功能,React如何机智的实现呢?

如何“捕获”错误

让我们先实现第一点:捕获用户代码抛出的错误。

但是不能使用try catch,因为这会让Pause on exceptions失效。

解决办法是:监听windowerror事件。

根据GlobalEventHandlers.onerror MDN[1],该事件可以监听到两类错误:

  • js运行时错误(包括语法错误)。window会触发ErrorEvent接口的error事件
  • 资源(如<img><script>)加载失败错误。加载资源的元素会触发Event接口的error事件,可以在window上捕获该错误

实现开发环境使用的wrapperDev

// 开发环境wrapper
function wrapperDev(func) {function handleWindowError(error) {// 收集错误交给Error Boundary处理}window.addEventListener('error', handleWindowError);func();window.removeEventListener('error', handleWindowError);
}

func执行时抛出错误,会被handleWindowError处理。

但是,对比生产环境wrapperPrdfunc抛出的错误会被catch,不会影响后续代码执行。

function wrapperPrd(func) {try {func();} catch(e) {// ...处理错误}
}

开发环境func内如果抛出错误,代码的执行会中断。

比如执行如下代码,finish会被打印。

wrapperPrd(() => {throw Error(123)})
console.log('finish');

但是执行如下代码,代码执行中断,finish不会被打印。

wrapperDev(() => {throw Error(123)})
console.log('finish');

如何在不捕获用户代码抛出错误的前提下,又能让后续代码的执行不中断呢?

如何让代码执行不中断

答案是:通过dispatchEvent触发事件回调,在回调中调用用户代码

根据EventTarget.dispatchEvent MDN[2]

不同于DOM节点触发的事件(比如click事件)回调是由event loop异步触发。

通过dispatchEvent触发的事件是同步触发,并且在事件回调中抛出的错误不会影响dispatchEvent的调用者(caller)。

让我们继续改造wrapperDev

首先创建虚构的DOM节点、事件对象、虚构的事件类型:

// 创建虚构的DOM节点
const fakeNode = document.createElement('fake');
// 创建event
const event = document.createEvent('Event');
// 创建虚构的event类型
const evtType = 'fake-event';

初始化事件对象,监听事件。在事件回调中调用用户代码。触发事件:

function callCallback() {fakeNode.removeEventListener(evtType, callCallback, false); func();
}// 监听虚构的事件类型
fakeNode.addEventListener(evtType, callCallback, false);// 初始化事件
event.initEvent(evtType, false, false);// 触发事件
fakeNode.dispatchEvent(event);

完整流程如下:

function wrapperDev(func) {function handleWindowError(error) {// 收集错误交给Error Boundary处理}function callCallback() {fakeNode.removeEventListener(evtType, callCallback, false); func();}const event = document.createEvent('Event');const fakeNode = document.createElement('fake');const evtType = 'fake-event';window.addEventListener('error', handleWindowError);fakeNode.addEventListener(evtType, callCallback, false);event.initEvent(evtType, false, false);fakeNode.dispatchEvent(event);window.removeEventListener('error', handleWindowError);
}

当我们调用:

wrapperDev(() => {throw Error(123)})

会依次执行:

  1. dispatchEvent触发事件回调callCallback
  2. callCallback内执行到throw Error(123),抛出错误
  3. callCallback执行中断,但调用他的函数会继续执行。
  4. Error(123)window error handler捕获用于Error Boundary

其中步骤2使Pause on exceptions不会失效。

步骤3、4使得错误被捕获,且不会阻止后续代码执行,模拟了try catch的效果。

总结

不得不说,React这波操作真细啊。

我们实现的迷你wrapper还有很多不足,比如:

  • 没有针对不同浏览器的兼容
  • 没有考虑其他代码也触发window error handler

React源码的完整版wrapper,见这里[3]

关注魔术师卡颂,了解更多React源码相关知识。

参考资料

[1]

GlobalEventHandlers.onerror MDN: https://developer.mozilla.org/zh-CN/docs/Web/API/GlobalEventHandlers/onerror

[2]

EventTarget.dispatchEvent MDN: https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/dispatchEvent

[3]

这里: https://github.com/facebook/react/blob/master/packages/shared/invokeGuardedCallbackImpl.js#L63-L237

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

js优化工具:ECMAScript Cruncher

利用Thomas Loo开发的ECMAScript Cruncher(ESC 可以http://www.saltstorm.net/depo/esc/或51AJAX.com下载)。ESC是一个小巧的Window Shell脚本。 利用Thomas Loo开发的ECMAScript Cruncher(ESC 可以下载)。ESC是一个小巧的Window Shell脚本。运行ESC&#xff0c;必须使用Window…

ASP.NET 缓存 Cache

ASP.NET 提供一个功能完整的缓存引擎&#xff0c;页面可使用该引擎通过 HTTP 请求存储和检索任意对象.缓存的生存期与应用程序的生存期相同&#xff0c;也就是说&#xff0c;当应用程序重新启动时&#xff0c;将重新创建缓存。 将数据添加到缓存中 1。通过指定其键和值将项添加…

使用Jenkins从gitlab拉取代码并部署以及gitlab更新代码后自动构建

使用Jenkins从gitlab上拉取代码&#xff0c;使用ssh。 创建Jenkins服务器的SSH密钥对&#xff1b;为相应的Gitlab用户添加密钥对的公钥&#xff0c;相当于Jenkins拉取代码时&#xff0c;使用的是该Gitlab用户&#xff0c;拥有其相应的代码克隆权限&#xff1b;Jenkins添加密钥…

wincc版本升级_wincc组态软件下载

wincc7.4中文版这款组态软件是由西门子打造能够为用户们带来众多行业中的各项自动化工业制作效果&#xff0c;广泛运用到钢铁行业以及食品行业&#xff0c;印刷行业中&#xff0c;带给了用户们十分便捷的操作体验&#xff01;wincc中文版介绍由西门子推出的专业数据集与监控软件…

CentOS7.0下Hadoop2.7.3的集群搭建

集群服务器规划 使用3台CentOS-6.8虚拟机进行集群搭建 服务ip主机名称用户HDFSYARNhadoop1192.168.1.40hadoop1root NameNode,Datenode,SecondaryNameNodeResourceManager,NodeManager,hadoop2192.168.1.39hadoop2rootDatenodeNodeManagerhadoop3192.168.1.38hadoop3rootDate…

第五章(1)Libgdx应用框架之生命周期

生命周期 一个libgdx应用有一个良好定义的生命周期&#xff0c;管理应用的状态&#xff0c;比如创建&#xff0c;暂停和恢复&#xff0c;渲染和处理应用。 ApplicationListener 应用开发者通过实现ApplicationListener接口来调整生命周期&#xff1a; publicclassMyGameimpleme…

OnKeyPress事件和Javascript检测键盘输入

对于有些时候&#xff0c;我们需要检测用户键盘输入的键盘信息&#xff0c;来处理一些相应的事件。 这里田子建议使用OnKeyPress“”事件来处理。相类似的还有OnKeyUp和OnKeyDown事件&#xff0c;这些田子个人认为都不是特别的理想化。 假如&#xff0c;我们预定义一个方法fu…

ssh-copy-id 命令快速实现ssh远程免密登录

用ssh-copy-id将公钥复制到远程机器中 将本地公钥拷贝至 用户名为root的远程主机上 ssh-copy-id -i ~/.ssh/id_rsa.pub root192.168.150.128/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/var/jenkins_home/.ssh/id_rsa.pub" /usr/bin/ssh-copy…

智慧职教云答案在哪里找_职教云网课答案在线查询,职教云答案查询,智慧职教云答案在哪里找到...

[问答题] 简述“二重证据法”及其影响。[问答题] 西方古典史学有哪些传统&#xff1f;[问答题] 古典史学的一般特征&#xff1f;[问答题] 兰克被称为西方史学的泰斗&#xff0c;他对西方史学的贡献主要表现在&#xff1a;[问答题] 中世纪史学的贡献&#xff1f;[问答题] 人类学…

Win10配置VSCode+Opencv3(C++) GCC环境

环境清单&#xff1a; win10 _64位系统 VSCode&#xff1a;官网地址 Opencv&#xff1a;3.4.5 Cmake&#xff1a;3.9.0 MinGw&#xff1a;MinGW-W64 GCC-8.1.0&#xff08;x86_64-posix-seh&#xff09; MinGW配置&#xff1a; MinGW可以在线安装&#xff0c;也可以直接…

100C之13:他该如何存款?

Table of Contents 1 问题2 分析3 解决方案4 题后语问题 假设银行一年整存零取的月息为0.63%。现在某人手中有一笔钱&#xff0c;他打算在今后的五年中每年的年底取出1000元&#xff0c;到第五年刚好取完&#xff0c;请算出他存钱时应存多少钱&#xff1f; 分析 也比较简单&am…

c#生成一组不同的随机数的方法

代码 #region生成不同随机数的方法///<summary>///生成不同随机数的方法///</summary>///<param name"min">最小值</param>///<param name"max">最大值</param>///<param name"count">取xx个</par…

python实验二报告_20172304 2019-2020-2 《Python程序设计》实验二报告

20172304 2019-2020-2 《Python程序设计》实验二报告课程&#xff1a;《Python程序设计》班级&#xff1a; 1723姓名&#xff1a; 段志轩学号&#xff1a;20172304实验教师&#xff1a;王志强实验日期&#xff1a;2020年4月15日必修/选修&#xff1a; 公选课1.实验内容设计并完…

安装minikube

下载安装 kubectl sudo curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl && sudo chmod x kubectl && sudo mv kubectl /us…

网管必须了解的理光复印机相关故障现相之一

相信很多人都知道&#xff0c;网络管理员主要负责的是维护管理好局域网的正常运行。很有公司、企事业单位&#xff0c;IT部门负责的工作常常会包含各种周边设备的日常维护。这其中又以打印机、复印机为主。当然&#xff0c;很多时候不要求我们一定要懂得维修打印机、复印机&…

windows10 vscode 构建最强大的 Mingw C++ gcc 编译环境

工具准备 首先应该准备如下工具&#xff1a; 安装对应版本的Visual Studio Code。安装VS Code上的C扩展&#xff1a;C extension for VS Code。下载MinGW-w64&#xff0c;考虑到是外网资源&#xff0c;建议选择离线版本&#xff0c;在线安装比较慢&#xff0c;不太稳定。MinG…

mysql数据库关联练习_mysql数据库建立数据表的练习(附代码)

数据库操作和学习并不难&#xff0c;难的是如何在各种实际运用情况下编写SQL语句的实现。这个过程&#xff0c;需要大量的练习&#xff0c;那么从这里开始&#xff0c;我们来讲解实现。任务概述具体关系数据库如下&#xff1a;数据库名&#xff1a;教师数据库教师表(编号 char(…

Xml中SelectSingleNode方法中的xpath用法

最常见的XML数据类型有&#xff1a;Element, Attribute&#xff0c;Comment, Text.Element, 指形如<Name>Tom<Name>的节点。它可以包括&#xff1a;Element, Text, Comment, ProcessingInstruction, CDATA, and EntityReference.Attribute, 指在<Employee >中…

win10安装vmware tools + 无法拖拽文件解决

1、加载VMware Tools安装光盘 打开虚拟机VMware Workstation&#xff0c;启动 Ubuntu 系统 菜单栏 - 虚拟机 - 安装VMware Tools。 2、将安装文件提取到本地磁盘 打开加载的VMwareTools光盘&#xff0c;鼠标右键 VMwareTools-*.tar.gz 文件&#xff0c;提取到系统盘下的文件…

火狐最实用的几款插件介绍[含附件]

今天早上发现了FF插件的一遍文章&#xff0c;感觉非常实用&#xff0c;尤其是批量下载图片&#xff0c;以前这个是最头疼的&#xff0c;现在变得很简单了&#xff0c;开心&#xff0c;下来给大家也分享一下吧。 我总结最实用的如下&#xff1a; Firebug 前端开发利器DownThemA…