在 Javascript 中,即使从未抛出异常,使用 try-catch 块的开销是否很大?

如果没有异常被引发到任何一个 try-catch 块中,那么使用几个 try-catch 块是否“缓慢”?我的问题和 这个是一样的,不过是针对 JavaScript 的。

假设我有20个函数,其中包含 try-catch 块,另一个函数调用这20个函数中的每一个函数 他们都不会抛出异常。我的代码会因为这个 try-catch 块而执行得更慢还是执行得更差?

43337 次浏览

The try-catch block is said to be expensive. However if critical performance is not an issue, using it is not necessarily a concern.

The penalty IMO is:

  • readability
  • inappropriate in many cases
  • ineffective when it comes to async programming

Readability: plumbing your code with plenty of try-catch is ugly and distracting

inappropriate: it's a bad idea to insert such block if your code is not subject to exception-crash. Insert it only if you expect a failure in your code. Take a look at the following topic: When to use try/catch blocks?

Async: the try-catch block is synchronous and is not effective when it comes to async programming. During an ajax request you handle both the error and success events in dedicated callbacks. No need for try-catch.

Hope this helps,

R.

Are you doing typical CRUD UI code? Use try catches, use loops that go to 10000 for no reason sprinkled in your code, hell, use angular/ember - you will not notice any performance issue.

If you are doing low level library, physics simulations, games, server-side etc then the never throwing try-catch block wouldn't normally matter at all but the problem is that V8 didn't support it in their optimizing compiler until version 6 of the engine, so the entire containing function that syntactically contains a try catch will not be optimized. You can easily work around this though, by creating a helper function like tryCatch:

function tryCatch(fun) {
try {
return fun();
}
catch(e) {
tryCatch.errorObj.e = e;
return tryCatch.errorObj;
}
}
tryCatch.errorObj = {e: null};




var result = tryCatch(someFunctionThatCouldThrow);
if(result === tryCatch.errorObj) {
//The function threw
var e = result.e;
}
else {
//result is the returned value
}

After V8 version 6 (shipped with Node 8.3 and latest Chrome), the performance of code inside try-catch is the same as that of normal code.

The original question asked about the cost of try/catch when an error was not thrown. There is definitely an impact when protecting a block of code with try/catch, but the impact of try/catch will vanish quickly as the code being protected becomes even slightly complex.

Consider this test: http://jsperf.com/try-catch-performance-jls/2

A simple increment runs at 356,800,000 iterations per second The same increment within a try/catch is 93,500,000 iterations per second. That's on overhead of 75% due to try/catch. BUT, a trivial function call runs at 112,200,000 iterations per second. 2 trivial function calls run at 61,300,000 iterations per second.

An un-exercised try in this test takes slightly more time than one trivial function call. That's hardly a speed penalty that matters except in the inner-most loop of something really intense like an FFT.

The case you want to avoid is the case where an exception is actually thrown. That is immensely slower, as shown in the above link.

Edit: Those numbers are for Chrome on my machine. In Firefox there is no significant difference between an unexercised try and no protection at all. There's essentially zero penalty to using try/catch if no exception is thrown.

I attempt to provide an answer based on concrete benchmark results. For that I wrote a simple benchmark that compares try-catch to various if-else conditions from simple to more complex. I understand that benchmarks might change a lot depending on the platform. Please comment if you get different results. See the try-catch benchmark here.

First, I try to represent the test suite here in a compact manner. See the link above for full details. There are four test cases, referred later by (index):

  • (1) try-catch block that calls a function lib.foo with a bit of trigonometric math. No error is ever thrown.
  • (2) if-else block that checks the existence of the function by 'foo' in lib and then calls the function.
  • (3) if-else block that checks the existence of the function by typeof lib['foo'] === 'function' and then calls the function.
  • (4) if-else block that checks the existence of the function by Object.prototype.hasOwnProperty.call(lib, 'foo') and then calls the function.

I ran the benchmark a few times on Chrome 87. Although the actual numbers changed from time to time, the results were consistent and can be roughly summarised as follows:

  • The try-catch (1) and if-else (2) were almost equivalent in run time. The try-catch (2) was sometimes slower by 1% to 2%.
  • The if-else (3) was 75% slower than try-catch (1) or if-else (2).
  • The if-else (4) was 90% slower than try-catch (1) or if-else (2).

To clarify, 75% slower means that if the fastest case took 1.0 secs then the 75% slower execution took 1.75 seconds.

As a conclusion, using try-catch in the case where error is never thrown seems to be as efficient as checking any simple condition. If the condition has anything more complex, try-catch is significantly faster.

As a personal note, the conclusion is in line with what I was taught at university. Although it was in the context of C++ the same lesson seems to apply here. If I remember correctly, my lecturer said that try-block was designed to be very efficient, almost invisible efficiency-wise. However, it was the catch-block that was slow and I mean really slow. If an error was thrown then the handling with catch-block took hundreds or even thousands times longer than what could be achieved with if-else block. Therefore, keep your exceptions exceptional.