Javascript Try-Catch 性能对比。错误检查代码

IT技术 javascript try-catch
2021-02-19 16:55:30

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

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

6个回答

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

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 的时候和案例。如果除了你之外没有人能够阅读、理解或维护它,那么你的代码有多快并不重要。
2021-05-01 16:55:30
我会说在性能方面你可以用 try/catch 发疯。不知道 13 年的情况如何,但现在 jsperf 在 Firefox 和 chrome 上几乎没有表现出性能下降(不会打扰并主动拒绝在 IE 中进行测试)。我借用了这个测试并添加了我自己的代码片段来测试性能而没有捕获任何类型的错误:jsperf.com/native-try-catch-vs-custom-trycatch-loop/6
2021-05-04 16:55:30
说得好。我只想补充一点,除非您确实有性能问题,否则最好使您的代码可读。当您确实遇到性能问题时,首先要测量问题所在,然后再进行优化。否则,您可能会花费大量时间优化错误的东西。
2021-05-16 16:55:30

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

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/

这意味着很好,但似乎没有测试有问题的东西。
2021-05-01 16:55:30
上面的实现不再有效(jsFiddle 不喜欢document.writeln)。这是一个更新版本:jsfiddle.net/Mct5N
2021-05-03 16:55:30

把教条放在一边,对现在的答案不满意......

如果您的代码很少抛出异常,则在违规者周围放置一个 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 非常有用和快速。
2021-05-15 16:55:30

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

视情况而定。正如 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 的提要(通常不是使用深度嵌套的方法,这只是为了演示)获取数据。

当然,性能也很重要,但是现在 javascript 引擎足够快,以至于没有人可能会注意到差异,除非您打算每 10 毫秒或其他时间调用一次 getCustomer。