2010-07-10 6 views
28

Wäre es schneller, einfach Code in einen try-catch-Block zu schreiben, anstatt verschiedene Fehlerprüfungen durchzuführen?Javascript Try-Catch-Leistung Vs. Fehler beim Überprüfen des Codes

Zum Beispiel ..

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

Ich hoffe, meine Frage Sinn macht. Ich würde gerne klären. Vielen Dank!

+0

sogar 'Funktion isNull (c)' ist nutzlos, siehe meine Antwort ... :) – galambalazs

+3

Ich bin nicht vertraut mit YUI, ich hasse nur Code, der mich seitlich scrollen lässt! –

+0

Ich kenne mich auch nicht mit YUI aus, sondern suche nach null || undefined kann leicht mit Javascript – galambalazs

Antwort

46

„Programme müssen für die Menschen geschrieben werden zu lesen, und nur nebenbei für Maschinen ausführen zu.“

Abelson & Sussman, SICP, Vorwort zur ersten Ausgabe

immer für lesbar sind darauf ausgerichtet. Der Schlüssel ist daran zu denken ist:

Vermeiden try-catch in erfolgskritischen Funktionen und Schleifen

Überall sonst werden sie viel Schaden nicht tun. Benutze sie weise, benutze sie sparsam. Als eine Randnotiz, wenn Sie ältere Browser unterstützen möchten, haben sie möglicherweise keinen try-catch.

Aber wie ich sehe, missbrauchen Sie offensichtlich einige Funktionen zur Fehlerüberprüfung. Sie können die gewünschten Objekte und Eigenschaften von Objekten testen, bevor Sie sie anstelle einer komplexen Prüfung verwenden. Und:

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

als

if (projectPhaseId != null) 

zum Beispiel geschrieben werden ... So das obige Beispiel kann ziemlich lesbar sein, auch ohne Fänge zu versuchen. Sie scheinen missbrauchen YUI ein bisschen.

Ich würde wetten, dies funktioniert wie erwartet:

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 

} 

Wie kühlen ist das? :)

+9

Gut gemacht.Ich möchte nur hinzufügen, dass es am besten ist, wenn Sie tatsächlich ein Leistungsproblem haben, Ihren Code lesbar zu machen. Wenn Sie tatsächlich ein Leistungsproblem haben, messen Sie zunächst, wo das Problem liegt, und optimieren Sie es dann. Sonst verbringst du viel Zeit damit, die falschen Dinge zu optimieren. –

+11

+1 für '" Programme müssen geschrieben werden, damit die Leute sie lesen können, und nur zufällig für die Ausführung von Maschinen. "' –

+0

Dies ist eine ausgezeichnete Antwort. Es gibt Zeiten und Fälle für die Verwendung von Try-Catch, trotz des ständigen Kampfes der Open-Source-Gemeinschaft gegen Ausnahmebehandlungscode. Es spielt keine Rolle, wie schnell Ihr Code ist, wenn niemand außer Ihnen es lesen, verstehen oder pflegen kann. – whoblitz

3

Sicher, es macht kompakter Code, aber es reduziert Ihre Debug-Fähigkeit und macht Hinzufügen von graceful Fehler-Recovery oder nützliche Fehlermeldungen viel, viel, härter.

0

Beachten Sie, dass dies auch auf der Grundlage von Browsern variiert, aber insgesamt habe ich nichts über signifikante Leistungseinbußen für die Verwendung eines Try/Catch-Blocks gelesen. Aber es ist nicht gerade eine gute Übung, sie zu verwenden, weil Sie nicht wissen können, warum ein Problem fehlgeschlagen ist.

Hier ist eine interessante Diashow von einigen Aspekten der Javascript-Performance. Auf Folie 76 decken sie Try/Catch-Blöcke und die Auswirkungen auf die Performance ab. http://www.slideshare.net/madrobby/extreme-javascript-performance

1

Hängt von der Situation ab. Wie Galambalazs erwähnt, ist Lesbarkeit wichtig. Bedenken Sie:

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

im Vergleich zu:

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

Ich würde sagen, die zweite viel besser lesbar ist. Sie neigen dazu, Daten wie googles apis oder twitter's feeds zurück zu bekommen (normalerweise nicht mit tief verschachtelten Methoden, das ist nur zur Demonstration hier).

Natürlich ist auch die Leistung wichtig, aber heutzutage sind Javascript-Engines schnell genug, dass niemand einen Unterschied bemerken kann, es sei denn, Sie rufen getCustomer alle zehn Millisekunden oder so.

28

Warum nicht eine Faktenbasis für das Argument haben? Der folgende Code zeigt die Auswirkungen auf die Leistung:

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

Dieses JSFiddle Sie die Ausgabe in Firebug Lite Konsole zeigt: http://jsfiddle.net/Mct5N/

+2

Hey wow, du hast die Frage tatsächlich beantwortet ! :) –

+6

Die obige Implementierung funktioniert nicht mehr (jsFiddle mag 'document.writeln' nicht). Hier ist eine aktualisierte Version: http://jsfiddle.net/Mct5N/ – blong

+0

Dies bedeutete gut, aber scheint nicht das Ding in Frage zu testen. –

4

Platzierung Dogma zur Seite und nicht mit den Antworten hier im Moment ist zufrieden ...

Wenn Ihr Code selten Ausnahmen auslöst, funktioniert die Platzierung eines Try-Catch um den Täter gut, da es keinen zusätzlichen Overhead gibt, der die Ausnahme abfängt oder verhindert.

Wenn der Code normalerweise Ausnahmen basierend auf unvorhersehbaren Daten oder einem ähnlichen Szenario auslöst, erhöht die Platzierung einer Schutzmethode die Leistung erheblich, bis zu 20 Mal, wenn Ausnahmen häufig auftreten.

Wenn ich eine Herangehensweise empfehlen sollte, sollten Sie, wenn möglich, einfache Wächter benutzen, wenn keine tiefe Verschachtelung vorliegt. Verwenden Sie im Falle einer tieferen Verschachtelung eine Schutzmethode, die bei Bedarf durchlaufen kann.

Hier ist ein paar Tests von mir selbst, dass ich davon abstammen.

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

Szenarien vergleichen die folgenden aber in Schleifen:

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

Verwenden Sie lodash _.get als die beste Schutzmethode und lodash ist einfach unglaublich nützlich und schnell. –

0

Performance weise versuchen Beifang ist 20-50% langsamer als wenn Schecks (https://jsperf.com/throw-catch-vs-if-check/1).

So, Für seltene Verwendung, macht keinen großen Unterschied. Bei starker Beanspruchung kann dies etwas bewirken.

Allerdings halte ich es für eine schlechte Praxis, try-catch zu verwenden, wenn es bei Prüfungen möglich ist, außer es verringert die Lesbarkeit stark.

Verwandte Themen