在javascript中尝试捕获...这不是一个好习惯吗?


69

javascript中提供了try-catch块的规定。尽管使用Java或任何其他语言进行错误处理是强制性的,但我看不到有人在javascript中更大程度地使用它们。这不是一个好习惯,还是我们在javascript中不需要它们?


2
While in java or *any other language* it is mandatory to have error handling...- 并不是的。Java,是的,但是有很多语言不坚持尝试捕获(例如C#)。
Jim G.

这是因为您不能在异步环境中使用它们。我经常通过较低的抽象级别的同步代码来使用它们,例如,将某物转换为某物,等等...
inf3rno 2014年

我敢打赌,在服务器端代码中您会看到比客户端代码更多的try-catch。您不了解的大多数客户端代码都没有做任何重要的事情。在大多数应用程序和浏览器端的情况下,我认为最小化管道传输比从每个错误中恢复更为重要。
svidgen

还有另一种方法可以避免尝试使用may和Either(monads),我们在节点中编写了一个Web抓取器。我们能够使用它删除所有的try catch。
user93

Answers:


66

人们应该避免使用throw错误作为在应用程序中传递错误条件的方法。

throw语句仅应使用“因为这绝不会发生,崩溃和燃烧。请勿以任何方式优雅地恢复”

try catch 但是,在主机对象或ECMAScript可能引发错误的情况下使用。

例:

var json
try {
    json = JSON.parse(input)
} catch (e) {
    // invalid json input, set to null
    json = null
}

node.js社区中,建议您在回调中传递错误(因为错误仅发生在异步操作中)作为第一个参数

fs.readFile(uri, function (err, fileData) {
    if (err) {
        // handle
        // A. give the error to someone else
        return callback(err)
        // B. recover logic
        return recoverElegantly(err)
        // C. Crash and burn
        throw err
    }
    // success case, handle nicely
})

还有其他问题,例如try / catch确实很昂贵,而且很丑陋,根本无法与异步操作一起使用。

因此,由于同步操作不应引发错误,并且不适用于异步操作,因此除了主机对象或ECMAScript引发的错误外,没有人使用try catch


2
我不会说没有人使用try catch,在大多数情况下,这只是工作的错误工具。如果确实有特殊情况,则值得抛出Error,但它们之间相差无几。
zzzzBov 2012年

7
@zzzzBov对于案例C,崩溃和烧伤,抛出错误没有错。我只是认为您不应该发现错误并进行恢复。例如document.getElementById,当元素不存在时不抛出,而仅返回null。几乎所有情况都可以这样做
Raynos 2012年

4
@Raynos,从同步函数抛出是完全可以接受的,并且在该用例中是有意义的。将错误返回给回调是异步的,而引发错误是同步的。
sbartell

@Raynos对于避免在客户端(非节点环境)上的流控制中使用错误/异常有什么好的建议?我已经在StackOverflow上找到了这篇文章,但主要针对Java
13年

@ b.long简单。永远不要抛出错误。问题解决了。
Raynos

32

由于Javascript具有异步特性,因此在Java语言中进行尝试/捕获并不像在其他语言中那样防弹。请考虑以下代码段:

try {
    setTimeout(function() {
        do_something_that_throws();
    }, 1000);
}
catch (e) {
    alert("You won't see this!");
}

问题在于控制流trydo_something_that_throws()执行之前就离开了该块,因此永远不会捕获在回调中引发的错误。

因此,在许多情况下,try / catch基本上是不合适的,而且是否有人异步执行代码并不总是很明显。幸运的是,javascript带有其独特的单线程异步回调习惯用法以及对实际闭包的支持,提供了一种绝妙的选择:连续传递样式错误处理。只需将对任何错误的正确响应作为函数传递,例如:

setTimeout(function () {
    do_something_that_calls_err(function(err) {
        alert("Something went wrong, namely this: " + err);
    }),
    1000);

5
它也不是其他语言的防弹产品。将该代码移植到支持异步回调的任何语言,它也会失败。
雷诺斯

2
@Raynos:你是对的;但是,其他语言(或更确切地说,它们的支持文化)并不像Javascript那样将其大量购买到异步回调方法中,并且由于大多数其他环境的多线程性质,try / catch的缺点更加明显,并且在其他许多警告的包围下,人们自然会在多线程/异步回调情况下谨慎行事,并且更加意识到潜在的陷阱。
tdammers

这真的是由于JavaScript的“异步特性”引起的吗?据我所知,从理论上讲,可以使Try / Catch在词法上像标识符在词法上关闭的方式一样工作。它只是在JavaScript中碰巧无法正常工作。
Brian Gordon

2
在Node.js的> = 0.8值得注意的是有可能的try / catch异步,请参阅我的问题stackoverflow.com/questions/14301839/...
本杰明Gruenbaum

异步try-catch的唯一替代方法是异步连续传递样式吗?
CMCDragonkai 2015年

23

这些答案很多都有些陈旧,没有考虑到新的ES7功能asyncawait

使用async/,await您现在可以按需要获取异步控制流:

async function email(address) {
  try {
    // Do something asynchronous that may throw...
    await sendEmail({ to: address, from: 'noreply@domain.com`, subject: 'Hello' })
  } catch(err) {
    if (err instanceof SomeCustomError) {
      elegantlyHandleError(err)
    } else {
      throw err
    } 
  }
})

了解更多关于async/ await这里。您可以使用async/ await现在使用babel


但这类似于同步代码
Atul Agrawal

@AtulAgrawal是的,这就是重点。Async / await允许您以同步样式编写异步代码,这样就可以避免“回调地狱”并将大量的Promise链接在一起。我认为这是编写异步代码的一种非常有趣的方式
Dana Woodman

因此,如果我们要使异步代码同步,那么使用javascript有什么好处。因为大多数人由于其异步特性而使用javascript
Atul Agrawal

2
@AtulAgrawal可以兼得两者-享受语言的异步特性(因为上面的代码仍将异步执行-释放线程和所有代码),并剥夺了程序员更友好的编码风格的好处。不过,这并非没有问题……
ZenMaster

15

javascript中的try-catch与实现它们的任何其他语言一样有效和有用。主要原因之一是,它在javascript中的使用率不如其他语言。这与javascript被视为丑陋的脚本语言的原因相同,与人们认为javascript程序员不是真正的程序员的原因相同:

  • Javascript是一种难以置信的普遍语言

如此之多的人(由于浏览器唯一支持的语言)才暴露了javascript的事实,这意味着您那里有很多非专业的代码。当然,还有许多次要原因:

  • javascript中的某些内容是异步的,因此catch无法执行(异步操作)
  • 关于try-catch如何对性能产生巨大影响的讨论过分夸张。它对性能有一定影响,但是对于大多数代码而言,这是非常值得的。
  • 不幸的是,JavaScript的实现方式通常是无声地忽略错误(例如,将字符串自动更改为数字)

无论如何,都应该使用try-catch ,但是您当然应该学习如何正确使用它们 -像编程中的所有其他内容一样。


3
为什么你拒绝投票,哦,沉默的投票者呢?
user250878 2014年

同意 我认为公认的答案通常是正确的,但是除了处理本机对象外,还有充分的理由使用try-catch甚至throw。当引发错误而不是将错误传递给回调时,它会停止进一步执行,并将堆栈跳至无法处理的第一个块。这是一个很大的优势,对于同步操作(特别是涉及深层嵌套的情况)尤其有意义。认为异常是“坏的”(好吧,除了明显的原因之外)似乎很疯狂-它们实际上是具有独特的“功能”的非常有用的工具。
分号2014年

2
您是否曾经花时间阅读jQuery的源代码?除了公认的答案中提到的那些场景之外,几乎没有任何尝试捕获:特征检测和使用会引发错误的本机/主机对象。调用不使用try-catch的代码(例如jQuery)“不专业”似乎很愚蠢。老实说,我认为特别是来自Java的新Javascript程序员倾向于过度使用try-catch等语言功能。
Stijn de Witt 2015年

6

我认为,try..catchJavaScript中罕见的大部分原因是因为该语言对错误的容忍度很高。绝大多数情况可以通过使用代码检查,良好的默认设置和异步事件来处理。在某些情况下,仅使用模式即可防止出现问题:

function Foo() {
    //this may or may not be called as a constructor!!
    //could accidentally overwrite properties on window
}

function Bar() {
    if (!(this instanceof Bar)) {
        return new Bar();
    }
    //this will only work on Bar objects, and wont impact window
}

JS中不存在其他语言中导致异常发生的一些主要问题。绝大多数时间不需要类型转换。相反,首选方法通常是进行功能检查(强制使用特定的接口):

function doFoo(arg) {
    if (arg.foo) {
        arg.foo();
    } else {
        Bar.prototype.foo.call(arg);
    }
}

随着async/ await语言的加入,try..catch它变得越来越普遍。承诺是的异步形式try..catch,应该期望:

doSomething().then(
  doSomethingWithResult,
  doSomethingWithError
)

改为:

try {
  const result = await doSomething()
  doSomethingWithResult(result)
} catch (e) {
  doSomethingWithError(e)
}

3

可能在Javascript中未大量使用try / catch的另一个原因是该结构在Javascript的第一个版本中不可用...它是在以后添加的。

结果,某些较旧的浏览器不支持它。(实际上,这可能会在某些较旧的浏览器中引起解析器/语法错误,这比大多数其他类型的错误更难“防御性编程”。)

更重要的是,由于最初不可用,因此最初发布的Java脚本内置函数(在许多语言中称为“库”函数)不会使用它。(如果它不是“抛出”,而是在遇到问题时只返回“ null”,那么从someobject.somefunction()中“捕获”错误就不太好。)


另一个可能的原因是,try / catch机制一开始似乎并没有必要(而且似乎并没有那么有用)。仅当调用通常嵌套在多个级别时才真正需要它。仅返回某种ERRNO即可用于直接调用(尽管要使其在可用时真正有用,但大多数语言的最佳实践是在各处使用它而不仅仅是深度嵌套的调用)。由于最初希望Javascript逻辑小而简单(毕竟,它只是网页的附件:-),所以不希望将函数调用深度嵌套,因此似乎没有必要使用try / catch机制。


3
不不不,绝对不是这样。老式的引擎不再需要很长时间了,并且一般而言,javascript社区只要有理由就可以很快放弃老式的东西。我什至敢打赌,现在大多数javascript开发人员都是在IE6之后。
卡米洛·马丁

0

我相信它们使用的并不多,因为在Javascript客户端代码中抛出异常会使调试页面更加困难。

我通常不希望抛出异常,而宁愿显示警报框(即alert("Error, invalid...");

听起来可能很奇怪,但是如果客户正在使用您构建的页面而该页面引发异常,则Javascript错误会在客户端发生,除非该客户是精通技术的编码器,否则他将永远无法分辨你是什​​么问题。

他只会打电话给您说:“嘿,第X页不起作用!”,然后由您自己找出问题所在以及代码中的位置。

通过使用警报框,他更可能打来电话并说:“嘿,当我单击X页面的按钮A时,它显示一个框显示...”,相信我,这将很容易找到错误。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.