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

时间:2010-07-10 00:23:58

标签: javascript try-catch

将代码放入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;
    }
}

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

7 个答案:

答案 0 :(得分:46)

  

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

     

Abelson& Sussman,SICP,第一版序言

始终瞄准<​​strong>可读代码。要记住的关键是:

避免性能关键函数中的try-catch和循环

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

}

是多少? :)

答案 1 :(得分:28)

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

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/

答案 2 :(得分:4)

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

如果您的代码很少抛出异常,那么在犯罪者身边放置一个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');

答案 3 :(得分:3)

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

答案 4 :(得分:1)

取决于具体情况。由于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的apis或twitter的feed中获取这样的数据(通常不会使用深层嵌套的方法,这只是为了演示)。

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

答案 5 :(得分:0)

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

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

答案 6 :(得分:0)

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

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

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