Javascript Try-Catch Performance против. Ошибка проверки кода - PullRequest
30 голосов
/ 10 июля 2010

Было бы быстрее просто поместить код в блок 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 ]

46 голосов
/ 10 июля 2010

"Программы должны быть написаны для людей читать, и только случайно для Машины для выполнения. "

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

}

Как круто это что? :)

28 голосов
/ 05 мая 2011

Почему бы не иметь фактическую основу для аргумента?Следующий код демонстрирует влияние на производительность:

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/

4 голосов
/ 20 апреля 2015

Отложив догму и не удовлетворившись ответами здесь в данный момент ...

Если в вашем коде редко выдают исключения, размещение 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 голосов
/ 10 июля 2010

Конечно, это делает более компактный код, но уменьшает ваши возможности отладки и делает добавление изящного восстановления после ошибок или полезных сообщений об ошибках намного, намного сложнее.

1 голос
/ 10 июля 2010

Зависит от ситуации.Как упоминает галамбалаз, читабельность важна.Рассмотрим:

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

Я бы сказал, что второе намного более читабельно.Вы, как правило, получаете данные обратно из таких вещей, как API-интерфейсы Google или каналы Twitter (хотя обычно не с помощью глубоко вложенных методов, это просто для демонстрации).

Конечно, производительность также важна, но в наши дни JavaScriptДвижки достаточно быстры, чтобы никто не мог заметить разницу, если только вы не собираетесь вызывать getCustomer каждые десять миллисекунд или что-то в этом роде.

0 голосов
/ 09 ноября 2017

Производительность с точки зрения производительности на 20-50% медленнее, чем при проверках (https://jsperf.com/throw-catch-vs-if-check/1).

Так, для редкого использования не имеет большого значения. Для интенсивного использования это можетнекоторая разница.

Тем не менее, я считаю плохой практикой использовать try-catch, если это можно сделать с помощью проверок if, за исключением случаев, когда это значительно снижает читабельность.

0 голосов
/ 10 июля 2010

Имейте в виду, что это также зависит от браузеров, но в целом я ничего не читал о значительных потерях производительности за использование блока try / catch. Но их использование не совсем хорошая практика, потому что вы не можете сказать, почему проблема не удалась.

Вот интересное слайд-шоу с некоторыми соображениями о производительности javascript. На слайде 76 они охватывают блоки try / catch и влияние на производительность. http://www.slideshare.net/madrobby/extreme-javascript-performance

...