手机版

最佳JavaScript错误处理实践

时间:2021-09-08 来源:互联网 编辑:宝哥软件园 浏览:

不管你的技术水平如何,错误或异常都是应用程序开发人员生活的一部分。网络开发的不一致性留下了许多可能发生和已经发生错误的地方。解决方案的关键是处理任何不可预见的(或可预见的)错误,以控制用户体验。有了JavaScript,有许多技术和语言特性可以用来正确解决任何问题。在JavaScript中处理错误是危险的。如果你相信墨菲定律,你就会犯错误。在本文中,我将深入研究JavaScript中的错误处理。我会讲一些陷阱和好的做法。最后,我们将讨论异步代码处理和Ajax。

我认为JavaScript的事件驱动模型为这种语言增加了丰富的含义。我认为这个浏览器的事件驱动引擎和错误报告机制没有什么不同。每当发生错误,就相当于在某个时间点抛出一个事件。理论上,我们可以像处理普通事件一样,在JavaScript中处理错误事件。如果这听起来很奇怪,请专注于以下旅程。本文只关注客户端的JavaScript。

例子

本文中使用的代码示例可以在GitHub上找到,当前页面如下所示:

单击每个按钮都会引发错误。它模拟了类型错误类型的异常。下面是这样一个模块的定义和单元测试。

函数错误(){ var foo={ };return foo . bar();}首先,这个函数定义了一个空对象foo。请注意,bar()方法没有在任何地方定义。我们使用单元测试来验证这是否会导致错误。

它('抛出一个TypeError ',函数(){ short . throws(target,type error);});这个单元测试使用Mocha和Should.js库中的测试断言。Mocha是一个运行测试框架,should.js是一个断言库。如果你不熟悉他们,可以免费在线浏览他们的文档。测试用例通常以它(“描述”)开始,以应该中断言的通过或失败结束。使用这个框架的好处是单元测试可以在节点中完成,而不是在浏览器中。我建议您认真对待这些测试,因为它们验证了JavaScript中的许多关键基本概念。

如上所示,error()定义了一个空对象,然后尝试调用它的方法。因为这个对象中没有bar()方法,所以会抛出异常。相信我,在像JavaScript这样的动态语言中,任何人都可能犯这样的错误。

糟糕的示范

我们先来看看糟糕的错误处理方法。我的错误处理操作被抽象并绑定到按钮。下面是处理程序的单元测试:

函数BadHandler(fn){ try { return fn();} catch (e) { }返回null}此处理程序接收回调函数fn作为依赖项。然后在处理程序中调用这个函数。这个单元测试说明了如何使用这个方法。

它('返回一个没有错误的值',function(){ var fn=function(){ return 1;};var结果=目标(fn);result . short . equal(1);});它('返回一个有错误的null ',function(){ var fn=function(){ throw Error('随机错误');};var结果=目标(fn);应该(结果)。等于(空);});如您所见,如果出现问题,这种奇怪的处理方法将返回null。这个回调函数fn()指向一个有效的方法或错误。单击下面的“处理事件”来完成其余部分。

(函数(处理程序,炸弹){ var BadButton=document . GetElementBYID(' bad ');if(BadButton){ BadButton . AddEventListener(' click '),function(){ handler(bomb);console . log(‘想象一下,因为隐藏错误而被提升’);});}}(badHandler,错误));更糟糕的是,我刚刚得到的是一个空。这让我很困惑,当我想确定哪里出了问题。这种出错时保持沉默的策略涵盖了从用户体验设计到数据损坏的每一个环节。令人沮丧的是,我不得不花几个小时调试,但我看不到try-catch代码块中的错误。这种奇怪的处理隐藏了代码中的所有错误,并且它假设一切正常。这可以在一些不关注代码质量的团队中顺利实现。然而,这些隐藏的错误最终会迫使您花费数小时调试代码。在依赖调用堆栈的多层解决方案中,可以确定错误来自哪里。在极少数情况下,对尝试捕获进行故障静默处理可能是合适的。然而,这并不是处理错误的好办法。

这种失败的策略是沉默会促使你更好地处理代码中的错误。JavaScript提供了一种更优雅的方式来处理此类问题。

难以理解的计划

继续,我们来看看不容易理解的治疗方法。我将跳过DOM的紧密耦合部分。这部分和我们刚才看到的不良待遇没什么区别。重点是下面单元测试的异常处理部分。

函数uglyHandler(fn){ try { return fn();} catch (e) { throw Error('新错误');} }它('返回一个带有错误的新错误',function(){ var fn=function(){ throw new type error(' type error ');};short . throws(function(){ target(fn);},错误);});与刚才不好的治疗相比,有了很好的进展。调用堆栈中引发异常。我喜欢的是从堆栈中释放错误,这对调试有很大的帮助。如果抛出异常,解释器将逐级检查调用堆栈以找到下一个处理程序。这为在调用堆栈顶部处理错误提供了许多机会。很遗憾,我看不到原始的错误信息,因为这是一个不容易理解的错误。所以我必须沿着调用堆栈找到原始异常。但至少我知道在抛出异常的地方发生了错误。

这种不可读的错误处理是无害的,但会使代码难以理解。让我们看看浏览器是如何处理错误的。

调用栈

然后,抛出异常的一种方法是添加一个尝试.调用堆栈顶部的catch代码块。例如:

函数main(bomb){ try { bomb();} catch(e){//处理所有错误的事情}}但是还记得我说过浏览器是事件驱动的吗?是的,JavaScript中的异常只是一个事件。解释器在发生异常的当前上下文中停止程序,并抛出异常。为了证明这一点,我们编写了一个全局事件处理函数onerror。它看起来就像这样:

window.addEventListener('error '),函数(e){ var error=e . error;console.log(错误);});此事件处理程序捕获执行环境中的错误。错误事件会在不同的地方产生不同的错误。这种方法的重点是集中处理代码中的错误。就像其他事件一样,您可以使用全局处理函数来处理各种错误。这使得错误处理只有一个单一的目标,如果你遵循SOLID的原则(单一责任、开闭切换、Liskov替代替代、接口隔离接口分离和依赖反转)。您可以随时注册错误处理功能。解释器循环这些函数。代码从充满try的语句中释放出来.捕捉,并变得易于调试。这种方法的关键是像处理普通的JavaScript事件一样处理错误。

现在,有一种方法可以用全局处理程序显示调用堆栈。我们能用它做什么?毕竟,我们需要使用调用堆栈。

记录调用堆栈

调用栈在处理错误修复时非常有用。好消息是浏览器提供了这些信息。即使错误对象的堆栈属性目前不是标准的,但它在较新的浏览器中通常得到支持。

所以,我们能做的最酷的事情就是把它打印到服务器上:

window.addEventListener('error '),函数(e){ var stack=e . error . stack;var message=e . error . ToString();if(stack){ message=' \ n ' stack;} var xhr=new XMlhttprequest();xhr.open('POST ','/log ',true);xhr.send(消息);});这在代码示例中可能不明显,但是这个事件处理程序将由前面的错误代码触发。如上所述,每个处理程序都有一个单一的目的,这使得代码DRY(不重复自己不重复轮子)。我对如何在服务器上捕获这些消息很感兴趣。

以下是节点运行时的屏幕截图:

调用栈对调试代码非常有帮助。永远不要低估调用堆栈的作用。

异步处理

哦,处理异步代码很危险!JavaScript将异步代码带出当前的执行环境。这意味着以下尝试有问题.catch语句。

函数异步处理程序(fn){ try { setTimeout(function(){ fn();}, 1);} catch (e) {}}此单元测试包含以下内容:

it('不捕捉有错误的异常',function(){ var fn=function(){ throw new TypeError(' type error ');};failed promise(function(){ target(fn);})应该. be . rejectedwith(TypeError);});功能失败承诺(fn) {返回新承诺(功能(解决,拒绝){拒绝(fn)};});}我必须以承诺验证异常来结束此处理程序。请注意,虽然我的代码正在尝试中.catch,存在未处理的异常。是的,试试看.catch只在单个执行环境中工作。当抛出异常时,解释器的执行环境不再是当前的try-catch块。这种行为类似于Ajax调用。所以,现在有两个选择。另一种方法是在异步回调中捕获异常:

setTimeout(function(){ try { fn();} catch (e) { //处理此异步错误}},1);虽然这种方法很有用,但仍有很大的改进空间。首先,尝试.catch代码块出现在代码中的任何地方。事实上,在20世纪70年代,当他们被编程调用时,他们希望他们的代码后退。此外,V8引擎不鼓励在函数中使用try…catch代码块(V8是Chrome浏览器和Node使用的JavaScript引擎)。他们建议编写这些代码块来捕捉调用堆栈顶部的异常。

这告诉我们什么?如上所述,在任何执行环境中,全局错误处理程序都是必要的。如果你给窗口对象添加了一个错误处理程序,也就是说,你已经完成了!遵循干燥和固体的原则不是很棒吗?全局错误处理程序将保持您的代码可读和干净。

以下是服务器端异常处理打印的报告。请注意,如果使用示例中的代码,输出可能会略有不同,具体取决于您使用的浏览器。

这个处理程序甚至可以告诉我哪个错误来自异步代码。它告诉我错误来自setTimeout()处理程序。太酷了!

错误是每个应用程序的一部分,但是正确的错误处理不是。处理错误至少有两种方法。一是失败是沉默,即代码中忽略错误。另一种方法是快速发现和解决错误,即停止并重现错误。我想我已经说清楚了我同意哪一个,为什么。我的选择:不隐瞒问题。没有人会因为你程序中的意外而责怪你。这是可以接受的,中断,再现,给用户一个尝试。在一个不完美的世界里,给自己一个机会很重要。错误是不可避免的,你做什么来解决它们很重要。合理利用JavaScript的错误处理功能和自动灵活的解码,可以让用户的体验更加流畅,也让开发者的诊断更加容易。

版权声明:最佳JavaScript错误处理实践是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。