作者|Lukas Gisder-Dubé

译者|谢丽

本文将分三部分剖析 JavaScript 中的缺点,首先我们将理解缺点的一样平常情形,之后,我们将关注后端(Node.js + Express.js),末了,我们将重点看下如何处理 React.js 中的缺点。
选择这些框架,是由于它们是目前最盛行的,但是,你该当也能够将这些新创造运用到其他框架中吧!

html错误代码JavaScript毛病处置威望指南 React

继上一篇文章 (https://link.medium.com/MO32x55aNR) 之后,我想谈谈缺点。
缺点很好——我相信你以前听过这个说法。
乍一看,我们害怕缺点,由于缺点每每会涉及到在公开场合受到侵害或羞辱。
通过犯缺点,我们实际上学会了如何不去做某事,以及下次如何做得更好。

显然,这是关于从现实生活的缺点中学习。
编程中的缺点有点不同。
它们为我们供应了很好的特色来改进我们的代码,并见告用户什么地方出了问题(也可能是教他们如何修复它)。

GitHub(https://github.com/gisderdube/graceful-error-handling) 上供应了一个完全的样例项目。

JavaScript 缺点和一样平常处理

throw new Error('something went wrong') 会在 JavaScript 中创建一个缺点实例,并停滞脚本的实行,除非你对缺点做了一些处理。
当你作为 JavaScript 开拓者开启自己的职业生涯时,你自己很可能不会这样做,但是,你已经从其他库(或运行时)那里看到了,例如,类似“ReferenceError: fs 未定义”这样的缺点。

Error 工具

Error 工具有两个内置属性供我们利用。
第一个是,作为参数通报给 Error 布局函数,例如 new Error(“这是缺点”)。
你可以通过 message 属性访问:

const myError = new Error(‘请改进代码’)console.log(myError.message) // 请改进代码

第二个是缺点堆栈跟踪,这个属性非常主要。
你可以通过 stack 属性访问它。
缺点堆栈将为你供应历史记录(调用堆栈),从中可以查看哪个文件导致了缺点。
堆栈的上部也包括,然后是实际的堆栈,从间隔缺点发生最近的点开始,一贯到最外层“须要为缺点卖力”的文件:

Error: 请改进代码 at Object.<anonymous> (/Users/gisderdube/Documents/_projects/hacking.nosync/error-handling/src/general.js:1:79) at Module._compile (internal/modules/cjs/loader.js:689:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10) at Module.load (internal/modules/cjs/loader.js:599:32) at tryModuleLoad (internal/modules/cjs/loader.js:538:12) at Function.Module._load (internal/modules/cjs/loader.js:530:3) at Function.Module.runMain (internal/modules/cjs/loader.js:742:12) at startup (internal/bootstrap/node.js:266:19) at bootstrapNodeJSCore (internal/bootstrap/node.js:596:3)

抛出和处理缺点

现在,Error 实例本身不会导致任何结果,例如,new Error('...') 不会做任何事情。
当缺点被抛出时,就会变得更有趣。
然后,如前所述,脚本将停滞实行,除非你在流程中以某种办法对它进行了处理。
记住,是手动抛出错误,还是由库抛出错误,乃至由运行时本身(Node 或浏览器),都没有关系。
让我们看看如何在不同的场景中处理这些缺点。

try .... catch

这是最大略但常常被遗忘的缺点处理方法——多亏 async / await,它的利用现在又多了起来。
它可以用来捕获任何类型的同步缺点,例如,如果我们不把 console.log(b) 放在一个 try … catch 块中,脚本会停滞实行。

… finally

有时候,不管是否有缺点,代码都须要实行。
你可以利用第三个可选块 finally。
常日,这与在 try…catch 语句后面加一行代码是一样的,但它有时很有用。

异步性——回调

异步性,这是在利用 JavaScript 时必须考虑的一个主题。
当你有一个异步函数,并且该函数内部发生缺点时,你的脚本将连续实行,因此,不会立即涌现任何缺点。
当利用回调函数处理异步函数时(不推举),你常日会在回调函数中收到两个参数,如下所示:

如果有缺点,err 参数就等同于那个缺点。
如果没有,参数将是 undefined 或 null。
要么在 if(err) 块中返回某项内容,要么将其他指令封装在 else 块中,这一点很主要,否则你可能会得到另一个缺点,例如,result 可能未定义,而你试图访问 result.data,类似这样的情形。

异步性——Promises

处理异步性的更好方法是利用 Promises。
在这一点上,除了代码可读性更强之外,我们还改进了缺点处理。
只要有一个 catch 块,我们就不再须要太关注详细的缺点捕获。
在链接 Promises 时,catch 块捕获会自 Promises 实行或上一个 catch 块以来的所有缺点。
把稳,没有 catch 块的 Promises 不会终止脚本,但会给你一条可读性较差的,比如:

(node:7741) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: something went wrong(node:7741) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. /

因此,务必要在 Promises 中加入 catch 块。

回到 try … catch

随着 JavaScript 引入 async / await,我们回到了最初的缺点处理方法,借助 try … catch … finally,缺点处理变得非常大略。

由于这和我们处理“普通”同步缺点的方法是一样的,以是如果须要的话,更随意马虎利用浸染域更大的 catch 语句。

做事器端缺点的产生和处理

现在,我们已经有了处理缺点的工具,让我们看下,我们在实际情形下能用它们做什么。
后端缺点的产生和处理是运用程序至关主要的组成部分。
对付缺点处理,有不同的方法。
我将向你展示一个自定义 Error 布局函数和缺点代码的方法,我们可以轻松地通报到前端或任何 API 消费者。
构建后真个细节并不主要,基本思路不变。

我们将利用 Express.js 作为路由框架。
让我们考虑下最有效的缺点处理构造。
我们希望:

一样平常缺点处理,如某种回退,基本上只是说:“有缺点,请再试一次或联系我们”。
这并不是特殊聪明,但至少关照用户,有地方错了——而不是无限加载或进行类似地处理。
分外缺点处理为用户供应详细信息,让用户理解有什么问题以及如何办理它,例如,有信息丢失,数据库中的条款已经存在等等。

构建一个自定义 Error 布局函数

我们将利用已有的 Error 布局函数并扩展它。
继续在 JavaScript 中是一件危险的事情,但根据我的履历,在这种情形下,它非常有用。
我们为什么须要它?我们仍旧希望堆栈跟踪给我们一个很好的调试体验。
扩展 JavaScript 原生 Error 布局函数可以让我们方便地得到堆栈跟踪。
我们唯一要做的是添加代码(我们稍后可以通过缺点代码访问)和要通报给前真个状态(http 状态代码)。

如何处理路由

在完成 Error 的自定义之后,我们须要设置路由构造。
正如我指出的那样,我们想要一个单点真缺点处理,便是说,对付每一个路由,我们要有相同的缺点处理行为。
在默认情形下,由于路由都是封装的,以是 Express 并不真正支持那种办法。

为理解决这个问题,我们可以实现一个路由处理程序,并把实际的路由逻辑定义为普通的函数。
这样,如果路由功能(或任何内部函数)抛出一个缺点,它将返回到路由处理程序,然后可以传给前端。
当后端发生缺点时,我们可以用以下格式通报一个相应给前端——比如一个 JSON API:

{ error: 'SOME_ERROR_CODE', description: 'Something bad happened. Please try again or contact support.'}

准备好不知所措。
当我说下面的话时,我的学生总是生我的气:

如果你咋看之下并不是什么都懂,那没问题。
只要利用一段韶光,你就会创造为什么要那样。

顺便说一下,这可以称为自上而下的学习,我非常喜好。

路由处理程序便是这个样子:

我希望你能读下代码中的注释,我认为那比我在这里阐明更故意义。
现在,让我们看下实际的路由文件是什么样子:

在这些例子中,我没有做任何有实际哀求的事情,我只是假设不同的缺点场景。
例如,GET /city 在第 3 行结束,POST /city 在第 8 号结束等等。
这也适用于查询参数,例如,GET /city?startsWith=R。
实质上,你会有一个未处理的缺点,前端会收到:

{ error: 'GENERIC', description: 'Something went wrong. Please try again or contact support.'}

或者你将手动抛出 CustomError,例如:

throw new CustomError('MY_CODE', 400, 'Error description')

上述代码会转换成:

{ error: 'MY_CODE', description: 'Error description'}

既然我们有了这个俊秀的后端设置,我们就不会再把缺点日志泄露到前端,而总是返回有用的信息,解释涌现了什么问题。

确保你已经在 GitHub(https://github.com/gisderdube/graceful-error-handling) 上看过完全的库。
你可以把它用在任何项目中,并根据自己的须要来修正它!

向用户显示缺点

下一个也是末了一个步骤是管理前真个缺点。
这里,你要利用第一部分描述的工具处理由前端逻辑产生的缺点。
不过,后真个缺点也要显示。
首先,让我们看看如何显示缺点。
如前所述,我们将利用 React 进行演习训练。

把缺点保存在 React 状态中

和其他数据一样,缺点和缺点会变革,因此,你想把它们放在组件状态中。
在默认情形下,你想要在加载时重置缺点,以便用户第一次看到页面时,不会看到缺点。

接下来我们必须澄清的是不同缺点类型及与其匹配的可视化表示。
就像在后端一样,有 3 种类型:

全局缺点,例如,个中一个常见的缺点是来自后端,或者用户没有登录等。
来自后真个详细缺点,例如,用户向后端发送登录凭据。
后端答复密码不匹配。
前端无法进行此项验证,以是这样的信息只能来自后端。
由前端导致的详细缺点,例如,电子邮件输入验证失落败。

2 和 3 非常类似,虽然源头不一样,但如果你乐意,就可以在同样的 state 中处理。
我们将从代码中看下如何实现。

我将利用 React 的原生 state 实现,但是,你还可以利用类似 MobX 或 Redux 这样的状态管理系统。

全局缺点

常日,我将把这些缺点保存在最外层的有状态组件中,并渲染一个静态 UI 元素,这可能是屏幕顶部的一个赤色横幅、模态或其他什么东西,设计实现由你决定。

让我们看下代码:

正如你看到的那样,Application.js 中的状态存在缺点。
我们也有方法可以重置并改变缺点的值。
我们把值和重置方法通报给 GlobalError 组件,在点击'x'时,该组件会显示缺点并重置它。
让我们看看 GlobalError 组件:

你可以看到,在第 5 行,如果没有缺点,我们就不做任何渲染。
这可以防止我们的页面上涌现一个空的红框。
当然,你可以改变这个组件的外不雅观和行为。
例如,你可以将“x”更换为 Timeout,几秒钟后重置缺点状态。

现在,你已经准备好在任何地方利用全局缺点状态了,只是从 Application.js 把 _setError 向下通报,而且,你可以设置全局缺点,例如,当一个要求从后端返回了字段 error: 'GENERIC'。
例如:

如果你比较

处理详细的要求缺点

和全局缺点类似,我们也有位于其他组件内部的局部缺点状态,过程相同:

有件事要记住,打消缺点常日有一个不同的触发器。
用' x '删除缺点是没故意义的。
关于这一点,在发出新要求时打消缺点会更故意义。
你还可以在用户进行变动时打消缺点,例如当修正输入值时。

源于前真个缺点

如前所述,这些缺点可以利用与处理后端详细缺点相同的办法(状态)进行处理。
这次,我们将利用一个有输入字段的示例,只许可用户在实际供应以下输入时删除一个城市:

利用缺点代码实现缺点国际化

大概你一贯想知道为什么我们有这些缺点代码,例如 GENERIC ,我们只是显示从后端通报过来的缺点描述。
现在,随着你的运用越来越大,你就会希望征服新的市场,并在某个时候面临多种措辞支持的问题。
如果你到了这个时候,你就可以利用前面提到的缺点代码利用用户的措辞来显示恰当的描述。

我希望你对如何处理缺点有了一些理解。
忘掉 console.error(err),它现在已经是过去时了。
可以利用它进行调试,但它不应该涌如今终极的产品构建中。
为了防止这种情形,我建议你利用日志库,我过去一贯利用 loglevel,我对它非常满意。

英文原文

https://levelup.gitconnected.com/the-definite-guide-to-handling-errors-gracefully-in-javascript-58424d9c60e6