Javascript Try-Catch Performance Vs.检查代码时出错

时间:2020-12-06 14:15:03

Would it be faster to just put code inside a try-catch block instead of performing various error checks?

将代码放入try-catch块而不是执行各种错误检查会更快吗?

For example..

function getProjectTask(projectTaskId) {
    if (YAHOO.lang.isUndefined(projectTaskId) || YAHOO.lang.isNull(projectTaskId) && !YAHOO.lang.isNumber(projectTaskId)) {
        return null;
    }

    var projectPhaseId, projectPhaseIndex, projectTaskIndex, projectPhases, projectPhase, projectTask;

    if (!YAHOO.lang.hasOwnProperty(projectTaskPhaseMap, projectTaskId)) {
        return null;
    }

    projectPhaseId = projectTaskPhaseMap[projectTaskId];

    if (YAHOO.lang.isUndefined(projectPhaseId) || YAHOO.lang.isNull(projectPhaseId) || !YAHOO.lang.hasOwnProperty(scheduleData.ProjectPhasesMap, projectPhaseId)) {
        return null;
    }

    projectPhaseIndex = scheduleData.ProjectPhasesMap[projectPhaseId];
    if (YAHOO.lang.isUndefined(projectPhaseIndex) || YAHOO.lang.isNull(projectPhaseIndex) || !YAHOO.lang.hasOwnProperty(scheduleData.ProjectPhases[projectPhaseIndex])) {
        return null;
    }
    projectPhase = scheduleData.ProjectPhases[projectPhaseIndex];

    if (!YAHOO.lang.hasOwnProperty(projectPhase.ProjectTasksMap, projectTaskId)) {
        return null;
    }

    projectTaskIndex = projectPhase.ProjectTasksMap[projectTaskId];

    if (YAHOO.lang.isUndefined(projectTaskIndex) || YAHOO.lang.isNull(projectTaskIndex)) {
        return null;
    }

    projectTask = scheduleData.ProjectTasks[projectTaskIndex];
}

VS

function getProjectTask(projectTaskId) {
    try {
        projectPhaseId = projectTaskPhaseMap[projectTaskId];
        projectPhaseIndex = scheduleData.ProjectPhasesMap[projectPhaseId];
        projectPhase = scheduleData.ProjectPhases[projectPhaseIndex];
        projectTaskIndex = projectPhase.ProjectTasksMap[projectTaskId];
        projectTask = scheduleData.ProjectTasks[projectTaskIndex];

    }
    catch (e) {
        return null;
    }
}

I hope my question makes sense. I would be happy to clarify. Thank you!

我希望我的问题有道理。我很乐意澄清。谢谢!

7 个解决方案

#1


46  

"Programs must be written for people to read, and only incidentally for machines to execute."

“必须编写程序供人们阅读,并且只能偶然让机器执行。”

Abelson & Sussman, SICP, preface to the first edition

Abiz&Sussman,SICP,第一版序言

Always aim for readable code. The key thing to remember is:

始终以可读代码为目标。要记住的关键是:

Avoid try-catch in performance-critical functions, and loops

避免在性能关键函数和循环中使用try-catch

Anywhere else they won't do much harm. Use them wisely, use them sparingly. As a side note if you want to support older browsers they may not have try-catch.

在其他任何地方他们都不会造成太大伤害明智地使用它们,谨慎使用它们。作为旁注,如果您想支持旧浏览器,他们可能没有try-catch。

But as I see you clearly misuse some functions for error checking. You can test for the desired objects and properties of objects right before you use them instead of complex checking. And:

但正如我看到你明显滥用一些函数进行错误检查。您可以在使用对象之前测试所需的对象和属性,而不是复杂的检查。和:

if (YAHOO.lang.isUndefined(projectPhaseId) || YAHOO.lang.isNull(projectPhaseId))

can be written as

可写成

if (projectPhaseId != null)

for example... So the example above can be fairly readable even without try catches. You seem to misuse YUI a bit.

例如......所以上面的例子即使没有尝试捕获也可以相当可读。你似乎有点误用了YUI。

I would bet this works as expected:

我敢打赌这可以按预期工作:

function getProjectTask(projectTaskId) {

   var projectPhaseId    = projectTaskPhaseMap[projectTaskId],
       projectPhaseIndex = scheduleData.ProjectPhasesMap[projectPhaseId],
       projectPhase      = scheduleData.ProjectPhases[projectPhaseIndex];

  if (projectPhase == null) return null; // projectPhase would break the chain

  var projectTaskIndex  = projectPhase.ProjectTasksMap[projectTaskId],
      projectTask       = scheduleData.ProjectTasks[projectTaskIndex];

   return projectTask || null; // end of the dependency chain

}

How cool is that? :)

多么酷啊? :)

#2


28  

Why not have a fact basis for the argument? The following code demonstrates the performance impact:

为什么没有这个论点的事实基础?以下代码演示了性能影响:

var Speedy = function() {
    this.init();
};
Speedy.prototype = {
    init: function() {
        var i, t1;
        this.sumWith = 0;
        this.sumWithout = 0;
        this.countWith = 0;
        this.countWithout = 0;
        for (i = 0; i < 5; i++) {
            t1 = this.getTime();
            console.log("Using Try/Catch, Trial #" + (i + 1) );
                        console.log("started " + t1 );
            this.goTry(t1);
            this.countWith++;
        }
        for (i = 0; i < 5; i++) {
            t1 = this.getTime();
            console.log("W/out Try/Catch, Trial #" + (i + 1) );
            console.log("started  :" + t1 );
            this.goAlone(t1);
            this.countWithout++;
        }
        for (i = 5; i < 10; i++) {
            t1 = this.getTime();
            console.log("Using Try/Catch, Trial #" + (i + 1) );
            console.log("started  :" + t1);
            this.goTry(t1);
            this.countWith++;
        }
        for (i = 5; i < 10; i++) {
            t1 = this.getTime();
            console.log("W/out Try/Catch, Trial #" + (i + 1) );
            console.log("started  :" + t1);
            this.goAlone(t1);
            this.countWithout++;
        }
        console.log("---------------------------------------");
        console.log("Average time (ms) USING Try/Catch: " + this.sumWith / this.countWith + " ms");
        console.log("Average time (ms) W/OUT Try/Catch: " + this.sumWithout / this.countWithout + " ms");
        console.log("---------------------------------------");
    },

    getTime: function() {
        return new Date();
    },

    done: function(t1, wasTry) {
        var t2 = this.getTime();
        var td = t2 - t1;
        console.log("ended.....: " + t2);
        console.log("diff......: " + td);
        if (wasTry) {
            this.sumWith += td;
        }
        else {
            this.sumWithout += td;
        }
    },

    goTry: function(t1) {
        try {
            var counter = 0;
            for (var i = 0; i < 999999; i++) {
                counter++;
            }
            this.done(t1, true);
        }
        catch (err) {
            console.error(err);
        }
    },

    goAlone: function(t1) {
        var counter = 0;
        for (var i = 0; i < 999999; i++) {
            counter++;
        }
        this.done(t1, false);
    }
};

var s = new Speedy();

This JSFiddle will show you the output in firebug lite's console: http://jsfiddle.net/Mct5N/

这个JSFiddle将在firebug lite的控制台中显示输出:http://jsfiddle.net/Mct5N/

#3


4  

Placing dogma aside and not being satisfied with the answers here at the moment...

把教条放在一边,暂时不满意这里的答案......

If your code rarely throws exceptions, placing a try-catch around the offender performs well because there is no additional overhead in catching the exception or preventing it.

如果您的代码很少抛出异常,那么在犯罪者周围放置一个try-catch表现很好,因为在捕获异常或阻止它时没有额外的开销。

If the code commonly throws exceptions based on unpredictable data or some scenario similar to that, placing a guard method increases performance considerably, up to 20 times if exceptions occur often.

如果代码通常根据不可预测的数据或类似的情况抛出异常,那么放置一个保护方法可以大大提高性能,如果经常发生异常,则最多可以提高20倍。

If I were to advise an approach, use simple guard operators when possible if there isn't deep nesting. In cases of deeper nesting, use a guard method that can traverse through as needed.

如果我建议一种方法,如果没有深度嵌套,尽可能使用简单的防护操作员。在更深层次嵌套的情况下,使用可以根据需要遍历的保护方法。

Here's some testing of my own that I based this off of.

以下是对我自己的一些测试。

http://jsfiddle.net/92cp97pc/6/

Scenarios are comparing the following but in loops:

场景比较以下但循环:

var a;

// scenario 1 (always throws/catches)
try { a.b.c.d; }
catch(ex) { }

// scenario 2 (about 20 times faster than scenario 1)
guard(a, 'b', 'c', 'd');

// now no exceptions will occur
a = { b: { c: { d: true } } };

// scenario 3 (too fast to measure)
try { a.b.c.d; }
catch(ex) { }

// scenario 4 (.04 times slower than scenario 3)
guard(a, 'b', 'c', 'd');

#4


3  

Sure, it makes for more compact code, but it reduces your debug ability and makes adding graceful error-recovery, or useful error messages much, much, harder.

当然,它可以实现更紧凑的代码,但它会降低您的调试能力,并使添加优雅的错误恢复或有用的错误消息变得更加困难。

#5


1  

Depends on the situation. As galambalazs mentions readability is important. Consider:

取决于情况。由于galambalazs提到可读性很重要。考虑:

function getCustomer (id) {
  if (typeof data!='undefined' && data.stores && data.stores.customers 
      && typeof data.stores.customers.getById=='function') {
    return data.stores.customers.getById(id);
  } else {
    return null;
  }
}

compared to:

function getCustomer (id) {
  try {return data.stores.customers.getById(id);} catch (e) { return null; }
}

I'd say the second is much more readable. You tend to get data back like this from things like google's apis or twitter's feeds (usually not with deeply nested methods though, that's just here for demonstration).

我会说第二个更具可读性。你倾向于从google的apis或twitter的feed中获取这样的数据(通常不会使用深度嵌套的方法,这只是为了演示)。

Of course, performance is also important, but these days javascript engines are fast enough that nobody's likely to notice a difference, unless you're going to call getCustomer every ten milliseconds or something.

当然,性能也很重要,但是现在javascript引擎足够快,没有人会注意到差异,除非你每10毫秒或者其他什么时候调用getCustomer。

#6


0  

Keep in mind that this varies based on browsers as well but overall I have not read anything about significant performance penalties for using a try/catch block. But it's not exactly a good practice to get into using them because you are not able to tell why a problem failed.

请记住,这也取决于浏览器,但总体而言,我还没有读到任何有关使用try / catch块的重大性能损失的信息。但是开始使用它们并不是一个好习惯,因为你无法说明问题失败的原因。

Here is an interesting slide show of some javascript performance considerations. On slide 76 they cover try/catch blocks and the performance impact. http://www.slideshare.net/madrobby/extreme-javascript-performance

这是一个有趣的幻灯片放映的一些JavaScript性能考虑因素。在幻灯片76上,它们覆盖了try / catch块和性能影响。 http://www.slideshare.net/madrobby/extreme-javascript-performance

#7


0  

Performance wise try-catch is 20-50% slower than if checks (https://jsperf.com/throw-catch-vs-if-check/1).

性能明智的try-catch比检查(https://jsperf.com/throw-catch-vs-if-check/1)慢20-50%。

So, For rare usage, doesn't make much difference. For heavy usage, it might make some difference.

因此,对于罕见的使用,没有太大的区别。对于大量使用,它可能会有所不同。

However, I feel it's bad practice to use try-catch, if it can be done by if checks except if it greatly reduces readability.

但是,我觉得使用try-catch是不好的做法,如果可以通过if检查来完成,除非它大大降低了可读性。

#1


46  

"Programs must be written for people to read, and only incidentally for machines to execute."

“必须编写程序供人们阅读,并且只能偶然让机器执行。”

Abelson & Sussman, SICP, preface to the first edition

Abiz&Sussman,SICP,第一版序言

Always aim for readable code. The key thing to remember is:

始终以可读代码为目标。要记住的关键是:

Avoid try-catch in performance-critical functions, and loops

避免在性能关键函数和循环中使用try-catch

Anywhere else they won't do much harm. Use them wisely, use them sparingly. As a side note if you want to support older browsers they may not have try-catch.

在其他任何地方他们都不会造成太大伤害明智地使用它们,谨慎使用它们。作为旁注,如果您想支持旧浏览器,他们可能没有try-catch。

But as I see you clearly misuse some functions for error checking. You can test for the desired objects and properties of objects right before you use them instead of complex checking. And:

但正如我看到你明显滥用一些函数进行错误检查。您可以在使用对象之前测试所需的对象和属性,而不是复杂的检查。和:

if (YAHOO.lang.isUndefined(projectPhaseId) || YAHOO.lang.isNull(projectPhaseId))

can be written as

可写成

if (projectPhaseId != null)

for example... So the example above can be fairly readable even without try catches. You seem to misuse YUI a bit.

例如......所以上面的例子即使没有尝试捕获也可以相当可读。你似乎有点误用了YUI。

I would bet this works as expected:

我敢打赌这可以按预期工作:

function getProjectTask(projectTaskId) {

   var projectPhaseId    = projectTaskPhaseMap[projectTaskId],
       projectPhaseIndex = scheduleData.ProjectPhasesMap[projectPhaseId],
       projectPhase      = scheduleData.ProjectPhases[projectPhaseIndex];

  if (projectPhase == null) return null; // projectPhase would break the chain

  var projectTaskIndex  = projectPhase.ProjectTasksMap[projectTaskId],
      projectTask       = scheduleData.ProjectTasks[projectTaskIndex];

   return projectTask || null; // end of the dependency chain

}

How cool is that? :)

多么酷啊? :)

#2


28  

Why not have a fact basis for the argument? The following code demonstrates the performance impact:

为什么没有这个论点的事实基础?以下代码演示了性能影响:

var Speedy = function() {
    this.init();
};
Speedy.prototype = {
    init: function() {
        var i, t1;
        this.sumWith = 0;
        this.sumWithout = 0;
        this.countWith = 0;
        this.countWithout = 0;
        for (i = 0; i < 5; i++) {
            t1 = this.getTime();
            console.log("Using Try/Catch, Trial #" + (i + 1) );
                        console.log("started " + t1 );
            this.goTry(t1);
            this.countWith++;
        }
        for (i = 0; i < 5; i++) {
            t1 = this.getTime();
            console.log("W/out Try/Catch, Trial #" + (i + 1) );
            console.log("started  :" + t1 );
            this.goAlone(t1);
            this.countWithout++;
        }
        for (i = 5; i < 10; i++) {
            t1 = this.getTime();
            console.log("Using Try/Catch, Trial #" + (i + 1) );
            console.log("started  :" + t1);
            this.goTry(t1);
            this.countWith++;
        }
        for (i = 5; i < 10; i++) {
            t1 = this.getTime();
            console.log("W/out Try/Catch, Trial #" + (i + 1) );
            console.log("started  :" + t1);
            this.goAlone(t1);
            this.countWithout++;
        }
        console.log("---------------------------------------");
        console.log("Average time (ms) USING Try/Catch: " + this.sumWith / this.countWith + " ms");
        console.log("Average time (ms) W/OUT Try/Catch: " + this.sumWithout / this.countWithout + " ms");
        console.log("---------------------------------------");
    },

    getTime: function() {
        return new Date();
    },

    done: function(t1, wasTry) {
        var t2 = this.getTime();
        var td = t2 - t1;
        console.log("ended.....: " + t2);
        console.log("diff......: " + td);
        if (wasTry) {
            this.sumWith += td;
        }
        else {
            this.sumWithout += td;
        }
    },

    goTry: function(t1) {
        try {
            var counter = 0;
            for (var i = 0; i < 999999; i++) {
                counter++;
            }
            this.done(t1, true);
        }
        catch (err) {
            console.error(err);
        }
    },

    goAlone: function(t1) {
        var counter = 0;
        for (var i = 0; i < 999999; i++) {
            counter++;
        }
        this.done(t1, false);
    }
};

var s = new Speedy();

This JSFiddle will show you the output in firebug lite's console: http://jsfiddle.net/Mct5N/

这个JSFiddle将在firebug lite的控制台中显示输出:http://jsfiddle.net/Mct5N/

#3


4  

Placing dogma aside and not being satisfied with the answers here at the moment...

把教条放在一边,暂时不满意这里的答案......

If your code rarely throws exceptions, placing a try-catch around the offender performs well because there is no additional overhead in catching the exception or preventing it.

如果您的代码很少抛出异常,那么在犯罪者周围放置一个try-catch表现很好,因为在捕获异常或阻止它时没有额外的开销。

If the code commonly throws exceptions based on unpredictable data or some scenario similar to that, placing a guard method increases performance considerably, up to 20 times if exceptions occur often.

如果代码通常根据不可预测的数据或类似的情况抛出异常,那么放置一个保护方法可以大大提高性能,如果经常发生异常,则最多可以提高20倍。

If I were to advise an approach, use simple guard operators when possible if there isn't deep nesting. In cases of deeper nesting, use a guard method that can traverse through as needed.

如果我建议一种方法,如果没有深度嵌套,尽可能使用简单的防护操作员。在更深层次嵌套的情况下,使用可以根据需要遍历的保护方法。

Here's some testing of my own that I based this off of.

以下是对我自己的一些测试。

http://jsfiddle.net/92cp97pc/6/

Scenarios are comparing the following but in loops:

场景比较以下但循环:

var a;

// scenario 1 (always throws/catches)
try { a.b.c.d; }
catch(ex) { }

// scenario 2 (about 20 times faster than scenario 1)
guard(a, 'b', 'c', 'd');

// now no exceptions will occur
a = { b: { c: { d: true } } };

// scenario 3 (too fast to measure)
try { a.b.c.d; }
catch(ex) { }

// scenario 4 (.04 times slower than scenario 3)
guard(a, 'b', 'c', 'd');

#4


3  

Sure, it makes for more compact code, but it reduces your debug ability and makes adding graceful error-recovery, or useful error messages much, much, harder.

当然,它可以实现更紧凑的代码,但它会降低您的调试能力,并使添加优雅的错误恢复或有用的错误消息变得更加困难。

#5


1  

Depends on the situation. As galambalazs mentions readability is important. Consider:

取决于情况。由于galambalazs提到可读性很重要。考虑:

function getCustomer (id) {
  if (typeof data!='undefined' && data.stores && data.stores.customers 
      && typeof data.stores.customers.getById=='function') {
    return data.stores.customers.getById(id);
  } else {
    return null;
  }
}

compared to:

function getCustomer (id) {
  try {return data.stores.customers.getById(id);} catch (e) { return null; }
}

I'd say the second is much more readable. You tend to get data back like this from things like google's apis or twitter's feeds (usually not with deeply nested methods though, that's just here for demonstration).

我会说第二个更具可读性。你倾向于从google的apis或twitter的feed中获取这样的数据(通常不会使用深度嵌套的方法,这只是为了演示)。

Of course, performance is also important, but these days javascript engines are fast enough that nobody's likely to notice a difference, unless you're going to call getCustomer every ten milliseconds or something.

当然,性能也很重要,但是现在javascript引擎足够快,没有人会注意到差异,除非你每10毫秒或者其他什么时候调用getCustomer。

#6


0  

Keep in mind that this varies based on browsers as well but overall I have not read anything about significant performance penalties for using a try/catch block. But it's not exactly a good practice to get into using them because you are not able to tell why a problem failed.

请记住,这也取决于浏览器,但总体而言,我还没有读到任何有关使用try / catch块的重大性能损失的信息。但是开始使用它们并不是一个好习惯,因为你无法说明问题失败的原因。

Here is an interesting slide show of some javascript performance considerations. On slide 76 they cover try/catch blocks and the performance impact. http://www.slideshare.net/madrobby/extreme-javascript-performance

这是一个有趣的幻灯片放映的一些JavaScript性能考虑因素。在幻灯片76上,它们覆盖了try / catch块和性能影响。 http://www.slideshare.net/madrobby/extreme-javascript-performance

#7


0  

Performance wise try-catch is 20-50% slower than if checks (https://jsperf.com/throw-catch-vs-if-check/1).

性能明智的try-catch比检查(https://jsperf.com/throw-catch-vs-if-check/1)慢20-50%。

So, For rare usage, doesn't make much difference. For heavy usage, it might make some difference.

因此,对于罕见的使用,没有太大的区别。对于大量使用,它可能会有所不同。

However, I feel it's bad practice to use try-catch, if it can be done by if checks except if it greatly reduces readability.

但是,我觉得使用try-catch是不好的做法,如果可以通过if检查来完成,除非它大大降低了可读性。