簡體   English   中英

Javascript Try-Catch Performance Vs.檢查代碼時出錯

[英]Javascript Try-Catch Performance Vs. Error Checking Code

將代碼放入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;
    }
}

我希望我的問題有道理。 我很樂意澄清。 謝謝!

“必須編寫程序供人們閱讀,並且只有偶然的機器才能執行。”

Abiz&Sussman,SICP,第一版序言

始終以可讀代碼為目標。 要記住的關鍵是:

避免在性能關鍵函數和循環中使用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

}

這有多 :)

為什么沒有這個論點的事實依據? 以下代碼演示了性能影響:

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/

把教條放在一邊,暫時不滿意這里的答案......

如果您的代碼很少拋出異常,那么在犯罪者周圍放置一個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');

當然,它可以實現更緊湊的代碼,但它會降低您的調試能力,並使添加優雅的錯誤恢復或有用的錯誤消息變得更加困難。

取決於情況。 由於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引擎足夠快,沒有人注意到差異,除非你打算每隔十毫秒調用一次getCustomer。

請記住,這也取決於瀏覽器,但總體而言,我還沒有讀到任何有關使用try / catch塊的重大性能損失的信息。 但是,使用它們並不是一個好習慣,因為你無法說明問題失敗的原因。

這是一個有趣的幻燈片放映的一些JavaScript性能考慮因素。 在幻燈片76上,它們覆蓋了try / catch塊和性能影響。 http://www.slideshare.net/madrobby/extreme-javascript-performance

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

因此,對於罕見的使用,沒有太大的區別。 對於大量使用,它可能會有所不同。

但是,我覺得使用try-catch是不好的做法,如果可以通過if檢查來完成,除非它大大降低了可讀性。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM