【问题标题】:Javascript Try-Catch Performance Vs. Error Checking CodeJavascript Try-Catch 性能对比错误检查代码
【发布时间】:2011-03-14 03:16:47
【问题描述】:

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

例如..

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;
    }
}

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

【问题讨论】:

  • 即使function isNull(c) 也没用,看我的回答... :)
  • 我也不熟悉 YUI,但检查 null || undefined 可以用 javascript 轻松完成
  • 抱歉,我重新格式化了代码部分以减少滚动。你们是对的。当一个简单的内置 javascript 语句就足够时,我尝试使用 YUI 库代码。 =P

标签: javascript try-catch


【解决方案1】:

“必须为人编写程序 阅读,并且只是偶然的 执行的机器。”

Abelson & Sussman,SICP,第一版序言

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

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

他们不会在其他任何地方造成太大伤害。明智地使用它们,在有意义的时候使用它们。

但正如我所见,您显然误用了某些功能进行错误检查。您可以在使用它们之前测试所需的对象和对象的属性,而不是进行复杂的检查。并且:

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

可以写成

if (projectPhaseId != null)

例如...所以即使没有尝试捕获,上面的示例也可以很好地阅读。你似乎有点误用 YUI。

我敢打赌这会按预期工作

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

}

这有多? :)

【讨论】:

  • 说得好。我只想补充一点,除非你真的有性能问题,否则最好让你的代码可读。当您确实遇到性能问题时,首先测量问题所在,然后再进行优化。否则你可能会花很多时间优化错误的东西。
  • 这是一个很好的答案。尽管开源社区一直在反对异常处理代码,但仍有使用 try-catch 的时间和案例。如果除了您之外没有人可以阅读、理解或维护它,那么您的代码有多快也没关系。
  • 我会说性能方面你可以用 try/catch 发疯。不知道 13 年的情况如何,但现在 jsperf 在 Firefox 和 chrome 上几乎没有表现出任何性能损失(不会打扰并积极拒绝在 IE 中进行测试)。我借用了这个测试并添加了自己的 sn-p 来测试性能,没有任何类型的错误捕获:jsperf.com/native-try-catch-vs-custom-trycatch-loop/6
【解决方案2】:

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

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

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

【讨论】:

    【解决方案3】:

    暂时搁置教条,对这里的答案不满意......

    如果您的代码很少抛出异常,那么在违规者周围放置一个 try-catch 效果会很好,因为在捕获或阻止异常时不会产生额外的开销。

    如果代码经常基于不可预测的数据或类似的场景抛出异常,放置一个保护方法会大大提高性能,如果经常发生异常,性能会提高 20 倍。

    如果我要建议一种方法,如果没有深层嵌套,请尽可能使用简单的守卫运算符。在嵌套更深的情况下,使用可以根据需要遍历的守卫方法。

    这是我自己的一些测试,我以此为基础。

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

    场景正在比较以下但在循环中:

    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');
    

    【讨论】:

    • 使用 lodash _.get 作为其最好的保护方法,lodash 非常有用且快速。
    【解决方案4】:

    为什么论证没有事实依据?以下代码演示了性能影响:

    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();
    

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

    【讨论】:

    • 上面的实现不再有效(jsFiddle 不喜欢 document.writeln )。这是更新版本:jsfiddle.net/Mct5N
    • 这很好,但似乎没有测试有问题的东西。
    【解决方案5】:

    视情况而定。正如 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;
      }
    }
    

    相比:

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

    我想说第二个更具可读性。您倾向于从 google 的 api 或 twitter 的提要之类的东西中获取数据(尽管通常不是使用深度嵌套的方法,但这只是为了演示)。

    当然,性能也很重要,但如今的 javascript 引擎已经足够快,以至于没有人可能会注意到其中的差异,除非您打算每隔 10 毫秒左右调用一次 getCustomer。

    【讨论】:

      【解决方案6】:

      请记住,这也因浏览器而异,但总的来说,我没有读到任何关于使用 try/catch 块的显着性能损失的信息。但是使用它们并不是一个好习惯,因为您无法说出问题失败的原因。

      这是一个有趣的幻灯片,展示了一些 javascript 性能注意事项。在幻灯片 76 上,它们涵盖了 try/catch 块和性能影响。 http://www.slideshare.net/madrobby/extreme-javascript-performance

      【讨论】:

        【解决方案7】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-05-26
          • 1970-01-01
          • 2020-07-11
          • 2022-12-21
          • 2011-02-04
          • 1970-01-01
          • 1970-01-01
          • 2020-08-17
          相关资源
          最近更新 更多