Chyba JavaScriptu při zpracování anti-vzoru

Jednou z oblastí, o které se domnívám, že chybí dostatek diskuzí, je zpracování chyb v JavaScriptu. Zatímco mnoho úvah se obvykle věnuje zpracování chyb v serverovém softwaru, včetně chybových protokolů a monitorovacích systémů, u JavaScriptu je na totéž kladen velmi malý důraz. Snažil jsem se o tom zvýšit povědomí svou přednáškou o Ajax Experience, Enterprise JavaScript Error Handling, kde jsem probíral přístupy k řešení chyb a také běžné zdroje chyb.

Jedním z mých návrhů v přednášce bylo poskytnout pro vaši aplikaci režim ladění. Myšlenka spočívá v tom, že produkční režim skrývá chyby JavaScriptu před uživatelem a vhodně je zpracovává, zatímco režim ladění umožňuje, aby se chyby dostaly až na úroveň prohlížeče a byly hlášeny jako obvykle. To druhé je samozřejmě důležité pro účely ladění. Když se chyba objeví v prohlížeči, máte možnost ladit se všemi okolními kontextovými informacemi. Vzor, který jsem navrhl ve svém projevu, vypadá takto:

function doSomething(value){
    try {
        process(value);
    } catch (ex){
        if (debugMode){
            throw ex;
        } else {
            log(1, "doSomething(): " + ex.message);
        }
    }
}

Myšlenka je taková, že chyba je zachycena a v závislosti na režimu udělá něco vhodného. Jak se často ve vývoji stává, nyní jsem zjistil, že to není nejlepší přístup a ve skutečnosti přináší bolestivé důsledky.

Pokud dojde k chybě v process() , tato chyba je zachycena a vyvolána z doSomething() , což naruší zásobník hovorů. Chyba je nyní příliš daleko od skutečné události, aby byla užitečná pro ladění. Všechny kontextové informace, které by mohly vést k řešení, se po ukončení provádění process() ztratí . Představte si, že máte ladicí program nastavený na přerušení při všech chybách:při použití tohoto kódu by došlo k přerušení na řádku obsahujícím throw ex když opravdu chcete, aby se zlomil uvnitř process() protože v tom je skutečný problém.

Nyní to považuji za chybu při zpracování anti-vzoru, vzor, ​​který spíše brání užitečnému ladění, než aby jej umožňoval. Vzor, který nyní doporučuji, je úplně odstranit try-catch příkaz v režimu ladění. To umožňuje normální provádění kódu a povede to ke správnému umístění zásobníku volání, když dojde k chybě. Existuje několik způsobů, jak tohoto vzoru dosáhnout, prvním je poněkud ošklivě vypadající podmíněný příkaz:

function doSomething(value){
    if (debugMode){
        process(value);
    } else {
        try {
            process(value);
        } catch (ex){
            log(1, "doSomething(): " + ex.message);
        }
    }
}

Druhý, pravděpodobně elegantnější přístup je jednoduše nahradit celou funkci založenou na režimu provádění:

var doSomething = debugMode ?
    function(value){
        process(value);
    } :
    function(value){
        try {
            process(value);
        } catch (ex){
            log(1, "doSomething(): " + ex.message);
        }
    };

Toto je můj preferovaný přístup, protože eliminuje kontrolu debugMode pokaždé, když je funkce provedena. Tento přístup lze navíc snadno automatizovat. Předpokládejme, že máte jeden nebo více objektů a chcete, aby všechny jejich metody měly ve výrobě obal pro zachycení chyb. Následující kód toho dosáhne poměrně snadno:

//by Nicholas C. Zakas (MIT Licensed)
function productionize(object){

    var name,
        method;

    for (name in object){
        method = object[name];
        if (typeof method == "function"){
            object[name] = function(name, method){
                return function(){
                    try {
                        return method.apply(this, arguments);
                    } catch (ex) {
                        log(1, name + "(): " + ex.message);
                    }
                };

            }(name, method);
        }
    }
}

Tento kód iteruje vlastnosti objektu a nahradí každou funkci jinou funkcí obsahující příslušný mechanismus zpracování chyb. Funkci můžete použít takto:

var system = {
    fail: function(){
        throw new Error("Oops!");
    }
};

function log(severity, message){
    alert(severity + ":" + message);
}

if (!debugMode){
    productionize(system);
}

system.fail();   //error is trapped!

Tento vzor zachycování chyb vám dobře poslouží ve složitých prostředích, kde může být obtížné vystopovat chyby. Ujistit se, že chyba je vyvolána ze správného místa, je prvním krokem při ladění problému.