2010-07-10 15 views
28

¿Sería más rápido poner el código dentro de un bloque try-catch en lugar de realizar varias comprobaciones de error?Javascript Try-Catch Performance vs. Código de comprobación de error

Por ejemplo ..

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

espero que mi pregunta tiene sentido. Me complacería aclararlo. ¡Gracias!

+0

incluso 'la función isNull (c)' es inútil, ver mi respuesta ... :) – galambalazs

+3

No estoy familiarizado con YUI, ¡solo odio el código que me hace desplazarme de lado! –

+0

Tampoco estoy familiarizado con YUI, pero estoy buscando null || indefinido se puede hacer fácilmente con javascript – galambalazs

Respuesta

46

"Los programas deben ser escritos para las personas de leer, y sólo incidentalmente para máquinas para ejecutar."

Abelson & Sussman, SICP, prólogo a la primera edición

siempre tratan de legible código. La clave para recordar es:

Evitar try-catch en funciones de rendimiento crítico, y los bucles

cualquier otro lugar que no van a hacer mucho daño. Úselos sabiamente, úselos con moderación. Como nota al margen si desea admitir navegadores más antiguos, es posible que no tengan try-catch.

Pero como veo claramente mal uso algunas funciones para la comprobación de errores. Puede probar los objetos y las propiedades deseadas de los objetos justo antes de usarlos en lugar de realizar una comprobación compleja. Y:

if (YAHOO.lang.isUndefined(projectPhaseId) || YAHOO.lang.isNull(projectPhaseId)) 

se puede escribir como

if (projectPhaseId != null) 

por ejemplo ... Así que el ejemplo anterior puede ser bastante legible incluso sin tratar capturas. Parece que mal uso YUI un poco.

apostaría esto funciona como se esperaba:

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 

} 

Cómo fresco es eso? :)

+9

Bien puesto.Me gustaría agregar que, a menos que tenga un problema de rendimiento, es mejor hacer que su código sea legible. Cuando realmente tiene un problema de rendimiento, primero mida dónde está el problema, solo luego optimícelo. De lo contrario, puede pasar mucho tiempo optimizando las cosas incorrectas. –

+11

+1 para '" Los programas deben escribirse para que las personas los lean, y solo de manera incidental para que las máquinas los ejecuten ". –

+0

Esta es una respuesta excelente. Hay momentos y casos para usar try-catch, a pesar del continuo grito de batalla de la comunidad de código abierto contra el código de manejo de excepciones. No importa cuán rápido sea su código, si nadie puede leerlo, comprenderlo o mantenerlo, excepto usted. – whoblitz

3

Claro, hace que el código sea más compacto, pero reduce la capacidad de depuración y hace que la agregación de recuperación de error elegante, o mensajes de error útiles mucho, mucho, más difícil.

0

Tenga en cuenta que esto también varía en función de los navegadores, pero, en general, no he leído nada acerca de las penalizaciones de rendimiento significativas al utilizar un bloque try/catch. Pero no es exactamente una buena práctica usarlas porque no puede decir por qué falló un problema.

Aquí hay una diapositiva interesante de algunas consideraciones de rendimiento de JavaScript. En la diapositiva 76 cubren bloques de prueba/captura y el impacto en el rendimiento. http://www.slideshare.net/madrobby/extreme-javascript-performance

1

Depende de la situación. Como galambalazs menciona la legibilidad es importante. Considere:

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

en comparación con:

function getCustomer (id) { 
    try {return data.stores.customers.getById(id);} catch (e) { return null; } 
} 

yo diría que el segundo es mucho más fácil de leer. Tiendes a obtener datos como este de cosas como las API de Google o los feeds de Twitter (generalmente no con métodos profundamente anidados, eso es solo aquí para la demostración).

Por supuesto, el rendimiento también es importante, pero en la actualidad los motores de búsqueda de JavaScript son lo suficientemente rápidos como para que nadie note la diferencia, a menos que llame a getCustomer cada diez milisegundos o algo así.

28

¿Por qué no tener una base fáctica para el argumento? El código siguiente muestra el impacto de rendimiento:

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(); 

Este jsFiddle le mostrará la salida en la consola de Firebug Lite: el dogma http://jsfiddle.net/Mct5N/

+2

Hola, de verdad que respondiste la pregunta ! :) –

+6

La implementación anterior ya no funciona (jsFiddle no le gusta 'document.writeln'). Aquí hay una versión actualizada: http://jsfiddle.net/Mct5N/ – blong

+0

Esto tenía buenas intenciones, pero parece que no está probando la cosa en cuestión. –

4

La colocación de un lado y no estar satisfecho con las respuestas aquí en este momento ...

Si su código rara vez arroja excepciones, colocar un try-catch alrededor del ofensor funciona bien porque no hay gastos adicionales para capturar la excepción o evitarla.

Si el código comúnmente arroja excepciones basadas en datos impredecibles o en algún escenario similar al anterior, la colocación de un método de protección aumenta considerablemente el rendimiento, hasta 20 veces si se producen excepciones a menudo.

Si tuviera que aconsejar una aproximación, use operadores de protección simples cuando sea posible si no hay una anidación profunda. En casos de anidamiento más profundo, use un método de protección que pueda atravesar según sea necesario.

He aquí algunas pruebas mías de las que basé para esto.

http://jsfiddle.net/92cp97pc/6/

escenarios están comparando los siguientes, pero en bucles:

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'); 
+0

use lodash _.get ya que es el mejor método de protección y lodash es asombrosamente útil y rápido. –

0

En cuanto al rendimiento try-catch es un 20-50% más lento que si los controles (https://jsperf.com/throw-catch-vs-if-check/1).

Por lo tanto, Para uso poco frecuente, no hace mucha diferencia. Para un uso intensivo, podría hacer alguna diferencia.

Sin embargo, creo que es una mala práctica usar try-catch, si se puede realizar mediante checks pero a menos que reduzca en gran medida la legibilidad.