Каква е правилната детайлност на улавянето на грешки в JavaScript?

Използвах да поставям try...catch във всеки метод на моите JS класове:

var MyConstructor = function() {
    this.init = function() {
        try {
            // the method code...
        } catch(error) {
            // the error manager log actions
        }
    };
    // other methods, with the same try/catch usage
};

По този начин, запазвайки интерфейса на кода сравнително прост, мислех, че всяка грешка в моя код щеше да бъде уловена и регистрирана/управлявана.

var myInstance = new MyConstructor();

Вместо това, достатъчно ли е един глобален catch блок на скрипт? Грижа за всяка възможна (или забележителна) грешка, струва ми се достатъчно да знам всяка грешка, възникваща в приложението:

// no try...catch inside the classes, but only a global try...catch per script:
try {
    var myInstance = new MyConstructor();
} catch(error) {
    /*
        log in the needed environment
        (e.g. client-side, via console, or to the server...)
    */
}

Търсих и прочетох теми в Stackoverflow и забележителни онлайн ресурси за управление на грешки в JavaScript.
В тази фаза се интересувам от намирането на най-добрия начин за намиране на всички грешки повече от управлението им за изящно поведение на потребителски интерфейс.
Не е ли правилният подход? Отворен съм за всякакви предложения.


person yodabar    schedule 14.01.2013    source източник


Отговори (2)


Основното правило е, че трябва да се запитате "Кой трябва да се справи с проблема логично?" и се придържайте към това.

Важното е да запомните, че когато пишете част от кода, вие всъщност пишете договор, описващ как битове и части от код си взаимодействат. Например във вашия случай, когато създавате екземпляр на MyConstructor, защо ще се провали? Какво обещание се прави, когато се генерира? Най-важното е, кой трябва да се справи с неуспеха му?

Някои примери

Да кажем, че например имаме клас Car и екземпляри на Car имат метод drive(x).

Когато извикате drive(x), Car се мести x места надясно.

Действието drive(x) може да се провали, например ако Car вече е на ръба на екрана или Car няма гориво.

Току-що дефинирахме drive като „Колата се движи на x места надясно“, което означава, че колата очаква да може да завърши действието на шофиране и невъзможността да го завърши е логично изключение. В този случай е съвсем очевидно, че този, който обработва изключението, е повикващият задвижването, а не самата кола, тъй като не би трябвало да знае в каква среда се движи. Дори по-добре, обаждащият се не трябва да се опитва да кара колата от ръба или без гориво по какъвто и да е начин.

В друг случай

Да кажем, че в същия пример Environment е клас, който съдържа автомобили и има метод moveCars(), който премества всички автомобили в средата според някаква вътрешна логика. Environment се използва от програма, която очаква да съдържа цялата логика на движение. Използваме някакъв алгоритъм в moveCars(), който трябва да ни увери, че колите няма да се сблъскат.

Може да имаме някакъв краен случай, за който не сме се сетили в нашия moveCars() метод, или такъв, който не трябва да се случва поради някакво предположение, но потребителят към средата очаква той да съдържа цялата логика на преместване, което означава, че когато възникне изключение, трябва да се справи сам.

И така, каква е общата стратегия?

Трябва да обработвате изключения въз основа на отговорността, която има компонентът, изпълняващ кода.

person Benjamin Gruenbaum    schedule 14.01.2013

Този въпрос всъщност не се ограничава до JavaScript. Правилната детайлност на улавянето на изключения зависи от правилната детайлност на модулите и функциите, които потенциално имат изключение. Добре проектираният модул или функция трябва да бъде обвързан с добре дефиниран договор (DbC). Докато договорът е изрично установен, проблемът с обработката на изключения ще стане много по-лесен.

Три важни въпроса относно договорите са: Какво очаква договорът? Какво гарантира договорът? Какво поддържа договорът? За пример приемете, че функцията divide(a, b) връща a / b. Тази функция очаква, че b е различно от нула (предварително условие), ако b случайно е нула, тази функция < strong>трябва да хвърли изключение вместо catch изключение. Тъй като отговорност на обаждащия е да гарантира валидността на предаващия аргумент, който е част от договора. Освен това, всички други грешки, които могат да възникнат във функцията divide, трябва да бъдат уловени вътре, защото това е нейна собствена отговорност. Като друга част от договора, divide обещава да върне стойност (т.е. коефициент), която умножена по b трябва да е равна на a (това се нарича последусловие).

Обработката на изключения може да бъде сложна. Функция (или модул) може да избере да улови изключение, ако това е в рамките на нейната отговорност, или да избере да не улови, ако това е извън нейните задължения, или да избере първо да улови изключението, да го обработи и след това да хвърли отново на повикващия след опаковане на изключението като изключение на повикващия слой.

За да обобщим, обработката на изключения не е самостоятелен проблем, това е част от дизайна на цялата система. След като дизайнер разумно раздели сложна система на сравнително прости модули, всеки с добра абстракция/интерфейс и добре дефиниран договор/отговорност, как и къде да се борави с изключението трябва да е ясно от само себе си.

person Hui Zheng    schedule 14.01.2013