Aar*_*ius 12 javascript error-handling
我有一个抛出错误的库:
throw new Error('The connection timed out waiting for a response')
它可能会因多种不同的原因引发错误,并且用户很难以不同的方式以不同的方式处理错误而没有switching on error.message,这不是最佳的,因为该消息并不是真正用于编程决策。我知道很多人 subclass Error,但似乎过分了。相反,我正在考虑 (a)error.name用自定义名称覆盖:
const error = new Error('The connection timed out waiting for a response');
error.name = 'ConnectionTimeout';
throw error;
Run Code Online (Sandbox Code Playgroud)
或 (b) 设置error.code(不是标准属性):
const error = new Error('The connection timed out waiting for a response');
error.code = 'ConnectionTimeout';
throw error;
Run Code Online (Sandbox Code Playgroud)
有没有首选的方法?这些方法中的任何一种都令人不悦吗?这是我能找到的关于该主题的最接近的对话,但它似乎没有定论,并且可能与新约定过时了:https : //esdiscuss.org/topic/creating-your-own-errors
这是一个更大的答案(我自己的)的大幅增强的部分重印,其中大部分是不相关的。但我怀疑您正在寻找的是:
考虑这个糟糕的(但常见的)保护代码:
function add( x, y ) {
if(typeof x !== 'number')
throw new Error(x + ' is not a number')
if(typeof y !== 'number')
throw new Error(y + ' is not a number')
return x + y
}
Run Code Online (Sandbox Code Playgroud)
每次add使用不同的非数字调用时x,error.message都会有所不同:
add('a', 1)
//> 'a is not a number'
add({ species: 'dog', name: 'Fido' }, 1)
//> '[object Object] is not a number'
Run Code Online (Sandbox Code Playgroud)
在这两种情况下,我都做了同样的坏事:为x. 但错误信息却不同!这使得在运行时将这些情况分组在一起变得不必要的困难。我的例子甚至让人无法判断它是否有价值x或y冒犯!
这些问题非常普遍地适用于您从本机代码和库代码中收到的错误。我的建议是,如果可以避免的话,不要在自己的代码中重复它们。
我发现的最简单的补救措施就是始终使用静态字符串作为错误消息,并花一些时间为自己建立约定。这就是我所做的。
我抛出的大多数异常分为两类:
我发现每个类别都有自己的规则,因此可以得到很好的服务。
第一种情况,相关信息是:
与令人反感的值相关的所有错误消息都应该包括两个数据点,并且以足够一致的方式来促进流量控制(我认为这是您关心的问题),同时保持人类可以理解。理想情况下,您应该能够在grep文字消息的代码库中找到可能引发错误的每个位置(这对维护有很大帮助)。
根据这些准则,我将如何构建错误消息:
objection + space + topic
// e.g.
"unknown planetName"
Run Code Online (Sandbox Code Playgroud)
通常存在一组离散的反对意见:
String,但不是Number,但不是Date,但不是zipCode = '__!!@')我根据需要为各个应用程序补充了更专业的反对意见,但这组已经满足了我的大部分需求。
主题几乎总是出现在抛出的代码块中的文字变量名称。为了帮助调试,我认为不要以任何方式转换变量名(例如更改字母大小写)非常重要。
该系统会产生如下错误消息:
'missing lastName'
'unknown userId'
'unavailable player_color'
'forbidden emailAddress'
'non-numeric x'
Run Code Online (Sandbox Code Playgroud)
这些消息始终是由单个空格分隔的两个“单词”,因此:let [objection, topic] = error.message.split(' '),然后您可以执行诸如找出要设置为错误状态的表单字段之类的操作。
现在你已经有了一套可以让你清晰地报告问题的标签,你必须诚实,否则这些标签将变得毫无价值。
不要这样做:
objection + space + topic
// e.g.
"unknown planetName"
Run Code Online (Sandbox Code Playgroud)
是的,确实undefined不是一个数值,但保护子句不会评估是否x是一个数字。这跳过了一个逻辑步骤,作为聪明的人,很容易跳过它。但如果你这样做了,你的许多错误都将是谎言,因此毫无用处。
实际上需要做很多工作才能使函数能够正确表达每个细微差别。如果我想“全力以赴”,我就必须这样做:
'missing lastName'
'unknown userId'
'unavailable player_color'
'forbidden emailAddress'
'non-numeric x'
Run Code Online (Sandbox Code Playgroud)
对于更有趣的值类型,例如社会保障号或电子邮件地址,如果您想让您的函数区分每个可能的失败场景,您将需要一两个额外的保护子句。这通常是矫枉过正。
通常,您可以只使用一个保护条款来确认该值是一种可接受的事物,而不是费尽全力来确定您收到的是哪一种不可接受的事物。两者之间的区别在于懒惰的思维会导致不诚实的错误消息。
在我上面的例子中,如果我们这样做就可以了typeof x !== 'number';如果此函数无法区分undefined和任何其他非数字,我们可能不会失去任何有价值的东西。在某些情况下,您可能会关心;然后,“全力以赴”,一步一步缩小漏斗。
但由于您通常不会执行每个测试,因此您必须确保您选择的错误消息精确且准确地反映了您执行的一两个测试。
对于失败的操作,通常只有一个数据点:操作的名称。我使用这种格式:
operation + space + "failed"
// e.g.
"UserPreferences.save failed"
Run Code Online (Sandbox Code Playgroud)
通常,操作是例程的名称,与调用时的名称完全相同:
function add( x, y ) {
if(x === undefined)
throw new Error('non-numeric x') // bad!
}
Run Code Online (Sandbox Code Playgroud)
错误消息看起来像面向用户的文本,这通常会激活我们大脑的散文写作部分。这意味着你要问自己这样的问题:
这些问题中的每一个,以及无数其他类似的问题,都是指明错误道路的路标。
错误消息不是推文,不是给下一个开发人员的注释,不是新手开发人员的教程,也不是您将向用户显示的帮助消息。错误消息是开发人员可读的错误代码,句点。我不建议使用数字常量(就像 Windows 那样)的唯一原因是没有人会维护一个单独的文档来将每个错误号映射到人类可读的解释。
因此,每当你内心的创意作者发出声音并提出帮助你编写“完美”的错误消息时,请将该作者踢出房间并锁上门。积极排除任何可能属于“编辑风格”的内容。如果两个理性的人能够以不同的方式回答这个问题,那么解决方案不是选择一个,而是两者都不做。
所以:
这并不是纠正错误的唯一方法,但是这套约定确实可以轻松完成三件重要的事情:
我们一直在讨论错误消息的约定,但这忽略了抛出什么样的东西的问题。你可以扔任何东西,但你应该扔什么?
我认为您应该只抛出 的实例Error,或者在适当的时候抛出它的子类之一(包括您自己的自定义子类)。即使您不同意我关于错误消息的所有建议,这也是事实。
两个最大的原因是本机浏览器代码和第三方代码抛出 real Error,因此如果您希望使用单个代码路径和工具集处理所有这些,则必须执行相同的操作,并且因为该类Error做了一些有用的事情(例如捕获堆栈跟踪)。
(剧透:在内置子类中,我怀疑您最常用的是TypeError、SyntaxError和RangeError。)
不管你扔的是哪种,我认为只有一种方法可以Error正确地扔:
function add( x, y ) {
if(x === undefined) throw new Error('missing x')
if(typeof x !== 'number') throw new Error('non-numeric x')
}
Run Code Online (Sandbox Code Playgroud)
如果您想在抛出错误之前将任意数据附加到错误中,您可以这样做:
operation + space + "failed"
// e.g.
"UserPreferences.save failed"
Run Code Online (Sandbox Code Playgroud)
如果您发现自己经常这样做,则表明您可以使用自定义子类来获取额外的参数并将它们自动连接到适当的位置。
| 归档时间: |
|
| 查看次数: |
3453 次 |
| 最近记录: |