Schnellere JavaScript-Fehlerdiagnose mit Error.stack

IEBlog Deutsch

Blog des Internet Explorer-Entwicklerteams

Schnellere JavaScript-Fehlerdiagnose mit Error.stack

  • Comments 0

IE10 in der Windows 8 Consumer Preview unterstützt Error.stack, das Webentwicklern besonders bei schwer zu reproduzierenden Fehlern eine schnellere Diagnose und Behebung ermöglicht. Entwickler können mit den Funktionen der Webplattformen, die moderne Browser unterstützen, sehr leistungsfähige Apps erstellen. Unter Windows 8 beruht diese Leistungsfähigkeit sowohl auf Internet Explorer 10 als auch auf JavaScript-Apps im Metro-Stil. Die höhere Leistung und Komplexität dieser Apps bedeutet, dass Entwickler bessere Tools für die Fehlerdiagnose und -behandlung benötigen, wie beispielsweise Error.stack.

Debuggen von Anwendungen

Die strukturierte Fehlerbehandlung in JavaScript beruht auf throw und try/catch. Der Entwickler deklariert einen Fehler und übergibt die Ablaufsteuerung an eine Komponente des Programms, die die Fehlerbehandlung übernimmt. Beim Auslösen eines Fehlers wird die Kette von Aufrufen (auch als Aufrufliste bezeichnet), die zu der Fehlerstelle führt, von Chakra erfasst, dem JavaScript-Modul in Internet Explorer. Wenn das ausgelöste Objekt ein Error ist (oder eine Funktion, deren Prototypkette auf einen Error zurückführt), wird von Chakra eine Stapelüberwachung erstellt, eine lesbare Auflistung der Aufrufliste. Diese Auflistung wird auf dem Error-Objekt als stack-Eigenschaft dargestellt. Im stack sind die Fehlermeldung, Funktionsnamen und Informationen über den Quelldateispeicherort der Funktionen enthalten. Entwickler können mithilfe dieser Informationen eine schnelle Fehlerdiagnose durchführen, da sowohl die aufgerufene Funktion als auch die Codezeile, die den Fehler verursacht hat, angezeigt werden. Beispielsweise kann die Eigenschaft darauf hinweisen, dass ein an die Funktion übergebener Parameter NULL war oder einen ungültigen Typ aufwies.

Im folgenden Beispiel wird ein einfaches Skript beschrieben, dass die Entfernung zwischen den beiden Punkten (0, 2) und (12, 10) berechnet:

(function () {

'use strict';

function squareRoot(n) {

if (n < 0)

throw new Error('Cannot take square root of negative number.');

 

return Math.sqrt(n);

}

 

function square(n) {

return n * n;

}

 

function pointDistance(pt1, pt2) {

return squareRoot((pt1.x - pt2.x) + (pt1.y - pt2.y));

}

 

function sample() {

var pt1 = { x: 0, y: 2 };

var pt2 = { x: 12, y: 10 };

 

console.log('Distance is: ' + pointDistance(pt1, pt2));

}

 

try {

sample();

}

catch (e) {

console.log(e.stack);

}

})();

In diesem Script befindet sich ein Fehler: Die Differenzen der Komponenten werden nicht quadriert. Bei einigen Eingaben gibt die pointDistance-Funktion daher einfach falsche Ergebnisse zurück, bei anderen Eingaben wird ein Fehler verursacht. Zum besseren Verständnis der Stapelüberwachung untersuchen wir den Fehler mit den F12-Entwicklungstools auf der Registerkarte „Skript“:

Screenshot einer Stapelüberwachung der F12-Entwicklertools, in der ein Aufruf von console.log(e.stack) protokolliert wurde. „e“ stellt das Error-Objekt dar, das an die catch-Klausel eines try/catch-Blocks übergeben wurde.

Die Stapelüberwachung wird an die Konsole in der catch-Klausel ausgegeben. Das Error-Objekt stammt aus der squareRoot-Funktion. Dies ist direkt ersichtlich, da die Funktion am Anfang der Auflistung steht. Zum Debuggen des Problems muss die Stapelüberwachung nicht eingehend untersucht werden: Die Vorbedingung von squareRoot wurde verletzt. Der Grund hierfür wird auf der nächst höheren Ebene des Stapels ersichtlich: die Unterausdrücke im Aufruf von squareRoot sollten selbst Parameter für square sein.

Beim Debuggen kann mithilfe der stack-Eigenschaft Code identifiziert werden, um einen Haltepunkt festzulegen. Sie können die Aufrufliste auch auf andere Weise anzeigen: Wenn im Skriptdebugger beispielsweise der Modus [Break on caught exception] (Unterbrechung bei Ausnahmefehler) ausgewählt ist, können Sie die Aufrufliste mit dem Debugger untersuchen. Sie können in bereitgestellten Anwendungen problematischen Code mithilfe von try/catch umschließen, um fehlgeschlagene Aufrufe zu erfassen und auf dem Server zu protokollieren. Entwickler können sich dann die Aufrufliste ansehen, um Problembereiche besser isolieren zu können.

DOM-Ausnahmen und Error.stack

Ich habe bereits darauf hingewiesen, dass das ausgelöste Objekt entweder ein Error sein muss oder über die entsprechende Prototypkette auf einen Error zurückführt. Dies ist beabsichtigt: JavaScript unterstützt das Auslösen beliebiger Objekte und sogar Primitiver als Ausnahmen. Natürlich können diese ebenfalls erfasst und untersucht werden, sie wurden jedoch nicht eigens dafür entwickelt, Fehler- oder Diagnoseinformationen zu enthalten. Deshalb werden bei Auslösung nur Error-Objekte mit einer stack-Eigenschaft aktualisiert.

DOM-Ausnahmen sind zwar Objekte, besitzen jedoch keine auf Error zurückführende Prototypketten und weisen deshalb auch keine stack-Eigenschaft auf. Wenn Sie beispielsweise DOM-Manipulationen durchführen und Fehler bezüglich der JavaScript-Kompatibilität untersuchen möchten, können Sie Ihren DOM-Manipulationscode mit einem try/catch-Block umschließen und ein neues Error-Objekt in der catch-Klausel auslösen:

function causesDomError() {

try {

var div = document.createElement('div');

div.appendChild(div);

} catch (e) {

throw new Error(e.toString());

}

}

Es bleibt natürlich Ihnen überlassen, ob Sie dieses Muster verwenden möchten. Für die Entwicklung von Hilfsprogrammbibliotheken ist es gut geeignet. Berücksichtigen Sie insbesondere, ob der Code die DOM-Manipulation verbergen oder einfach eine Aufgabe ausführen soll. Wenn die DOM-Manipulation verborgen werden soll, sollte die Manipulation umschlossen und ein Error-Objekt ausgelöst werden.

Leistungsüberlegungen

Das Erstellen der Stapelüberwachung beginnt, wenn das Error-Objekt ausgelöst wird. Hierzu ist es erforderlich, den aktuellen Ausführungsstapel zu durchlaufen. IE sammelt beim Durchlaufen von besonders großen Stapeln (oder einer rekursiven Stapelkette) standardmäßig nur die 10 wichtigsten Stapelrahmen, um Leistungsprobleme zu vermeiden. Diese Einstellung kann jedoch konfiguriert werden, indem die statische Eigenschaft Error.stackTraceLimit auf einen anderen Wert festgelegt wird. Die Einstellung ist global und muss vor dem Auslösen des Error-Objekts geändert werden. Andernfalls wirkt sie sich nicht auf Stapelüberwachungen aus.

Asynchrone Ausnahmen

Wenn eine Stapelüberwachung von einem asynchronen Callback generiert wird (z. B. durch timeout, interval oder XMLHttpRequest), befindet sich am Ende der Aufrufliste der asynchrone Callback anstelle des Codes, durch den der asynchrone Callback erstellt wurde. Hierdurch ist fehlerhafter Code möglicherweise schwerer zu finden: Wenn Sie für mehrere asynchrone Callbacks die gleiche Callback-Funktion verwenden, kann es schwierig sein, zu bestimmen, welcher Callback den Fehler verursacht hat. Nehmen wir am vorherigen Beispiel eine kleine Änderung vor, sodass sample() nicht direkt, sondern über einen Timeout-Callback aufgerufen wird:

(function () {

'use strict';

function squareRoot(n) {

if (n < 0)

throw new Error('Cannot take square root of negative number.');

 

return Math.sqrt(n);

}

 

function square(n) {

return n * n;

}

 

function pointDistance(pt1, pt2) {

return squareRoot((pt1.x - pt2.x) + (pt1.y - pt2.y));

}

 

function sample() {

var pt1 = { x: 0, y: 2 };

var pt2 = { x: 12, y: 10 };

 

console.log('Distance is: ' + pointDistance(pt1, pt2));

}

 

setTimeout(function () {

try {

sample();

}

catch (e) {

console.log(e.stack);

}

}, 2500);

})();

Beim Ausführen dieses Ausschnitts wird die Stapelüberwachung erst nach einer kurzen Verzögerung generiert. Am Ende des Stapels wird in diesem Fall nicht Global Code, sondern Anonymous function angezeigt. Es handelt sich jedoch nicht tatsächlich um dieselbe anonyme Funktion, sondern um die an setTimeout übergebene Callback-Funktion. Da der Kontext der Verknüpfung des Callbacks verloren gegangen ist, können Sie möglicherweise nicht ermitteln, wodurch der Aufruf des Callbacks verursacht wurde. Wenn Sie ein Szenario in Betracht ziehen, in dem ein Callback für die Verarbeitung des click-Ereignisses für verschiedene Schaltflächen registriert ist, wissen Sie nicht, auf welchen Callback die Registrierung verweist. Diese Einschränkung ist jedoch nur geringfügig, denn in den meisten Fällen werden Problembereiche am Anfang des Stapels hervorgehoben.

Kennenlernen der Test Drive-Demo

Screenshot der Test Drive-Demo zu Error.stack

Sehen Sie sich diese Test Drive-Demo in IE10 unter Windows 8 Consumer Preview an. Sie können im Kontext von eval Code ausführen. Wenn ein Fehler auftritt, können Sie diesen untersuchen. Wenn Sie den Code in IE10 ausführen, können Sie außerdem Codezeilen hervorheben, indem Sie die Maus über die Fehlerzeilen in der Stapelüberwachung bewegen. Sie können in den Codebereich eigenen Code eingeben oder in der Liste verschiedene Beispiele auswählen. Weiterhin können Sie auch den Error.stackTraceLimit-Wert festlegen, wenn Sie die Codebeispiele ausführen.

Referenzmaterial finden Sie in der MSDN-Dokumentation über Error.stack und stackTraceLimit.

—Rob Paveza, Program Manager, Chakra Runtime