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;
}
}
Надеюсь, мой вопрос имеет смысл. Я был бы рад прояснить ситуацию.
Спасибо!
Ответы
Ответ 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, если это можно сделать с помощью проверок, за исключением случаев, когда это значительно снижает читаемость.