Javascript Try-Catch Performance vs. Codice di controllo degli errori

Sarebbe più veloce inserire il codice all’interno di un blocco try-catch invece di eseguire vari controlli degli errori?

Per esempio..

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

Spero che la mia domanda abbia un senso. Sarei felice di chiarire. Grazie!

“I programmi devono essere scritti affinché le persone possano leggere e solo incidentalmente per le macchine da eseguire.”

Abelson & Sussman, SICP, prefazione alla prima edizione

Cerca sempre un codice leggibile . La cosa fondamentale da ricordare è:

Evita il try-catch in funzioni e cicli critici per le prestazioni

Ovunque non faranno molto male. Usali saggiamente, usali con parsimonia. Come nota a margine, se vuoi supportare i browser più vecchi, potrebbero non avere il try-catch.

Ma come ti vedo chiaramente uso improprio alcune funzioni per il controllo degli errori. È ansible verificare gli oggetti e le proprietà degli oggetti desiderati prima di utilizzarli invece di un controllo complesso. E:

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

può essere scritto come

 if (projectPhaseId != null) 

per esempio … Quindi l’esempio sopra può essere abbastanza leggibile anche senza cercare catture. Sembri abusare un po ‘di YUI.

Scommetto che funziona come previsto:

 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 } 

Quanto è bello ? 🙂

Perché non avere una base di fatto per l’argomento? Il seguente codice dimostra l’impatto sulle prestazioni:

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

Questo JSFiddle ti mostrerà l'output nella console di firebug lite: http://jsfiddle.net/Mct5N/

Mettere da parte i dogmi e non essere soddisfatti delle risposte qui al momento …

Se raramente il tuo codice genera eccezioni, l’esecuzione di un try-catch attorno al colpevole funziona bene perché non c’è un sovraccarico aggiuntivo nell’ottenere l’eccezione o impedirla.

Se il codice genera di solito eccezioni basate su dati non prevedibili o su uno scenario simile, l’inserimento di un metodo di protezione aumenta notevolmente le prestazioni, fino a 20 volte se si verificano spesso eccezioni.

Se dovessi consigliare un approccio, usa, se ansible, operatori di guardia semplici, se non c’è un nidificazione profonda. In caso di nidificazione più profonda, utilizzare un metodo di guardia che può attraversare secondo necessità.

Ecco alcuni test per conto mio su cui ho basato questo.

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

Gli scenari stanno confrontando i seguenti, ma in loop:

 var a; // scenario 1 (always throws/catches) try { abcd; } 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 { abcd; } catch(ex) { } // scenario 4 (.04 times slower than scenario 3) guard(a, 'b', 'c', 'd'); 

Certo, rende più compatto il codice, ma riduce la tua capacità di debug e rende l’aggiunta di errori di recupero degli errori, o messaggi di errore utili molto, molto, molto più difficile.

Dipende dalla situazione. Come menziona il galambalaz, la leggibilità è importante. Prendere in considerazione:

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

rispetto a:

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

Direi che il secondo è molto più leggibile. Tendi a ottenere dati in questo modo da cose come le apis di Google o i feed di Twitter (di solito non con metodi profondamente nidificati, è solo qui per la dimostrazione).

Naturalmente, anche le prestazioni sono importanti, ma in questi giorni i motori javascript sono abbastanza veloci che nessuno potrebbe notare una differenza, a meno che non si chiami getCustomer ogni dieci millisecondi o qualcosa del genere.

Tieni presente che questo varia in base ai browser, ma nel complesso non ho letto nulla sulle penali significative delle prestazioni per l’utilizzo di un blocco try / catch. Ma non è esattamente una buona pratica entrare nel loro utilizzo perché non sei in grado di capire perché un problema ha avuto esito negativo.

Ecco una presentazione interessante di alcune considerazioni sulle prestazioni di javascript. Sulla diapositiva 76 coprono i blocchi try / catch e l’impatto sulle prestazioni. http://www.slideshare.net/madrobby/extreme-javascript-performance

Il saggio di prova delle prestazioni è più lento del 20-50% rispetto ai controlli ( https://jsperf.com/throw-catch-vs-if-check/1 ).

Quindi, per usi rari, non fa molta differenza. Per un uso intenso, potrebbe fare qualche differenza.

Tuttavia, ritengo che sia una ctriggers pratica usare il try-catch, se può essere fatto da controlli se non riducendo notevolmente la leggibilità.