Подтвердить что ты не робот

Javascript Try-Catch Performance Vs. Код проверки ошибок

Было бы проще просто поставить код внутри блока 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;
    }
}

Надеюсь, мой вопрос имеет смысл. Я был бы рад прояснить ситуацию. Спасибо!

4b9b3361

Ответ 1

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

Абельсон и Суссман, 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

}

Как cool это?:)

Ответ 2

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

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/

Ответ 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');

Ответ 4

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

Ответ 5

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

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 feeds (обычно не с глубоко вложенными методами, хотя, вот только для демонстрации).

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

Ответ 6

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

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

Ответ 7

Эффективный мудрый try-catch на 20-50% медленнее, чем если проверка (https://jsperf.com/throw-catch-vs-if-check/1).

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

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