Ošetření chyb

O čem si budeme povídat?

Krátce z historie práce s chybami

Z hlediska způsobu zpracování chyb je z našich tří jazyků VBScript ten nejbizarn>jší. Je to dáno tím, že staví na základech jazyka BASIC, který patří k jedn>m z prvních programovacích jazyků (kolem roku 1963). Způsob zpracování chyb v jazyce VBScript patří k t>m místům, ze kterých je zmín>né d>dictví jasn> vid>t. Pro naše účely to není špatné, protože mi to dává příležitost k vysv>tlení, proč VBScript používá práv> takový přístup. Vysv>tlíme si historii způsobu zpracování chyb od jazyka BASIC, přes Visual Basic, až k VBScript. Poté se podíváme na mnohem modern>jší přístup, který ukázkovým způsobem využívají JavaScript a Python.

V tradiční verzi jazyka BASIC byly se na všech řádcích programu psala čísla řádků. Přesun řízení se provád>l skokem na určitý řádek použitím příkazu GOTO. (Příklad jsme si ukázali v rámci tématu V>tvení.) Byl to v podstat> jediný možný způsob řízení. V prostředí s takovými vlastnostmi byl b>žný způsob ošetřování chyb založen na deklaraci prom>nné ERRORCODE, ve které se ukládala číselná hodnota. Když se v programu vyskytla chyba, nastavil se obsah prom>nné ERRORCODE na odpovídající hodnotu — nepodařilo se otevřít soubor, neslučitelnost typů, přetečení operátoru, a podobn>.

Uvedený přístup vedl k psaní kódu, který vypadal podobn>, jako následující úsek fiktivního programu:

1010 LET DATA = INPUT FILE
1020 CALL FUNKCE_PRO_ZPRACOVANI_DAT
1030 IF NOT ERRORCODE = 0 GOTO 5000
1040 CALL JINA_FUNKCE
1050 IF NOT ERRORCODE = 0 GOTO 5000
1060 REM POKRACUJ VE ZPRACOVANI TAKTO...
...
5000 IF ERRORCODE = 1 GOTO 5100
5010 IF ERRORCODE = 2 GOTO 5200
5020 REM DALSI PRIKAZY IF
...
5100 REM ZDE ZPRACUJ CHYBOVY KOD 1.
...
5200 REM ZDE ZPRACUJ CHYBOVY KOD 2.

Vidíme, že skoro polovina hlavního programu se zabývá zjišťováním, zda nenastala chyba. Časem byl zaveden o n>co elegantn>jší mechanismus, ve kterém se detekce chyba a jejich ošetření částečn> přesunulo do interpretu jazyka. Vypadal n>jak takto:

1010 LET DATA = INPUTFILE
1020 ON ERROR GOTO 5000
1030 CALL FUNKCE_PRO_ZPRACOVANI_DAT
1040 CALL JINA_FUNKCE
...
5000 IF ERRORCODE = 1 GOTO 5100
5010 IF ERRORCODE = 2 GOTO 5200

K popisu umíst>ní kódu pro ošetření chyby zde stačí jeden řádek. Pokud funkce narazila na chybu, musela i nadále nastavovat hodnotu prom>nné ERRORCODE, ale velmi se zjednodušil zápis (a čtení!) kódu.

No a co to má společného s námi? Je to docela prosté. Tento způsob zpracování chyb i nadále používá Visual Basic, ačkoliv čísla řádků byla nahrazena uživatelsky přív>tív>jšími náv>štími. VBScript — jako potomek jazyka Visual Basic — používá výrazn> ořezanou verzi téhož mechanismu. To ve svém důsledku znamená, že nám VBScript dává na vybranou: buď budeme ošeřovat chyby lokáln>, nebo je budeme zcela ignorovat.

Pokud se rozhodneme pro ignorování chyb, zapíšeme to takto:

On Error Goto 0  ' 0 říká "nikam neskákej"
NejakaFunkce()
NejakaJinaFunkce()
...

Pokud se rozhodneme pro lokální ošetření chyb, použijeme zápis:

On Error Resume Next
NejakaFunkce()
If Err.Number = 42 Then
   ' tady ošetři chybu
NejakaJinaFunkce()
...

Takový zápis vypadá trochu zpátečnicky. Ve skutečnosti prost> odráží výše popsaný historický proces.

V případ> výskytu chyby se interpret standardn> zachová tak, že uživateli zobrazí zprávu a zastaví provád>ní programu. Práv> toto se stane, pokud při určování způsobu zpracování chyby použijeme zápis Goto 0. To znamená, že zápisem Goto 0 vypínáme lokální řízení a říkáme interpretu, aby se zachoval obvyklým způsobem.

Předpis pro zpracování chyby obsahující Resume Next nám buď dovolí předstírat, že chyba vůbec nenastala, nebo si můžeme otestovat objekt pro popis chyby (je pojmenován Err). Zajímá nás zejména jeho číselná složka Number. (Jde o naprosto stejnou techniku, jako výše ukázané testování ERRORCODE.) Objekt Err nese ješt> další informační položky, které nám mohou pomoci vyrovnat se s nastalou situací mén> katastrofickým způsobem, než je zastavení programu. Můžeme například zjistit, v jakém míst> k chyb> došlo (v jakém objektu, v jaké funkci a podobn>). Můžeme také získat textový popis chyby, který můžeme uživateli vypsat jako součást hlášení o chyb>, nebo jej můžeme zapsat do log souboru. Typ chyby můžeme v objektu Err zm>nit použitím jeho metody Raise. Tuto metodu můžeme použít i při generování našich vlastních chyb, které nastaly v našich vlastních funkcích.

Jako příklad mechanismu ošetřování chyby v jazyce VBScript is uveďme případ, kdy dochází k d>lení nulou:

<script type="text/vbscript">
Dim x, y, vysledek
x = CInt(InputBox("Zadejte d>lence: "))
y = CInt(InputBox("Zadejte d>litele: "))
On Error Resume Next
vysledek = x/y
If Err.Number = 11 Then ' D>lení nulou
   vysledek = Null
End If
On Error GoTo 0 ' ošetřování chyb op>t vypneme
If VarType(vysledek) = vbNull Then
   MsgBox "CHYBA: Operace nemohla být provedena."
Else
   MsgBox CStr(x) & " d>leno " & CStr(y) & " je rovno " & CStr(vysledek)
End If
</script>

Upřímn> řečeno, uvedený přístup moc p>kný není. A zatímco obdivování dávné historie může být balzámem pro duši, moderní programovací jazyky, včetn> jazyků Python a JavaScript, nabízejí mnohem elegantn>jší způsoby ošetřování chyb. Podívejme se, o jaké mechanismy jde.

Ošetřování chyb v jazyce Python

Zpracování výjimek

V modern>jších programovacích prostředích se vyvinul alternativní způsob práce s chybami. Je znám pod pojmem ošetření výjimek a je založen na tom, že funkce vrhají (throw) nebo, jinými slovy, vyvolávají (raise) výjimky (exception [iksepšn]). Systém si pak zajistí vyskočení z aktuálního bloku kódu na nejbližší blok pro ošetření výjimky. V systému se nachází také blok kódu, který zachytí (catch) všechny výjimky, které dosud nebyly zpracovány n>kde jinde. Poté obvykle zobrazí chybové hlášení a ukončí b>h aplikace.

K velkým výhodám tohoto stylu ošetřování chyb patří mnohem lepší čitelnost hlavní funkčnosti programu. Je to dáno tím, že nedochází k mísení s kódem pro ošetřování chyb. Jinými slovy, v bloku kódu si můžeme číst aniž bychom byli jakkoliv nuceni číst kód pro ošetřování chyb.

Podívejme se, jak se uvedený styl programování používá v praxi.

Try/Catch

Blok kódu pro ošetření výjimek se trochu podobá bloku if...then...else:

try:
   # Zde se umístí hlavní část kódu programu.
except TypVyjimky:
   # Zde se bude zpracovávat jmenovaná výjimka.
except JinyTypVyjimky:
   # Zde se ošetřuje jiná výjimka.
else:
   # Zde umístíme úklidový kód, který se provede
   # v případ>, že nenastane žádná výjimka.

Python se pokouší provád>t příkazy mezi příkazy try a prvním except. Pokud dojde k chyb>, provád>ní příkazů v bloku za try se zastaví a skočí se dolů k příkazu except. Postupn> se procházejí jednotlivé příkazy except, dokud se nenajde ten, který odpovídá typu chyby (neboli výjimky). Pokud se nalezne, provede se blok bezprostředn> následujícího kódu. Pokud se nenalezne žádný odpovídající příkaz except, předává se nalezená chyba dál, do dalších (vyšších) úrovní programu. Pokud není nalezen odpovídající except, dostane se chyba až na úroveň interpretu jazyka Python, který chybu zachytí, zobrazí chybové hlášení a zastaví provád>ní programu. V našich programech jsme zatím pozorovali práv> takový projev, protože jsme zatím neum>li chybu zachytit a zpracovat sami.

Pokud v bloku try k žádným chybám nedojde, pak se provede blok za else. Ale v praxi se této možnosti využívá velmi zřídka. Poznamenejme, že příkaz except, u kterého není uveden žádný typ chyby, zachytí chyby všech typů, které dosud nebyly zpracovány. Využívání této formy příkazu se ale obecn> nepovažuje za dobrý nápad. Výjimku ovšem představuje jeho používání v nejvyšší úrovni programu, kdy chceme zabránit tomu, aby Python uživateli zobrazil velmi technickou podobu chybového hlášení. Obecná podoba příkazu except nám v tomto případ> umožní odchytit všechny dosud neošetřené chyby a zobrazit přív>tiv>jší hlášení o ukončování programu.

Za zmínku stojí, že součástí instalace Pythonu je i modul traceback, který nám umožní extrahovat různé doplňkové informace o zdroji chyby. To se nám může hodit při vytváření log souboru a pro podobné akce. Modulem traceback se zde zabývat nebudeme. V případ> potřeby naleznete popis jeho vlastností a použití ve standardní dokumentaci modulů.

A jak to vše skutečn> funguje si ukážeme na příkladu:

hodnota = raw_input("Zadej delitele: ")
try:
    hodnota = int(hodnota)
    print "42 / %d = %d" % (hodnota, 42 / hodnota)

except ValueError: 
    print "Hodnotu nelze prevest na cele cislo."

except ZeroDivisionError:
    print "Neni povolena nulova hodnota."

except: 
    print "Stalo se neco neocekavaneho."

else: 
    print "Program skoncil uspesne."

Pokud program spustíme a místo čísla zadáme n>jaký řet>zec, zobrazí se zpráva vypisovaná ve v>tvi ValueError (chyba hodnoty). Pokud zadáme nulu, zobrazí se zpráva pro ZeroDivisionError (chyba při d>lení nulou). Pokud zadáme platné číslo, zobrazí se výsledek a zpráva o úsp>šném ukončení programu.

Try/Finally

Existuje ješt> jeden typ bloku, který souvisí s výjimkami. Umožňuje zapsat kód pro úklid provád>ný i poté, co nastala chyba. Nazývá se try...finally (try [tray] = zkus, pokus se vykonat; finally [fajnly] = nakonec) a typicky se používá pro zavírání souborů, vyprazdňování vyrovnávacích pam>tí (buffer) na disk a podobn>. Blok finally je proveden vždy jako poslední nezávisle na tom, co se stane v sekci try. Pokud nenastane výjimka, prost> se provede. Pokud nastane výjimka, zapamatuje se její objekt, kód bloku finally se provede a zapamatovaná výjimka se znovu vyvolá.

try:
   # Kód, související s účelem programu.
finally:
   # V této části provádíme úklidové akce nezávisle
   # na tom, zda v bloku try nastala chyba či nikoliv.

Síla této konstrukce se projeví při kombinaci s blokem try/except. Konkrétní volba pořadí zanoření t>chto bloků nepřináší významné výhody. Pořadí zpracování příkazů zůstává v obou případech stejné. Osobn> používám blok try/finally obvykle jako vn>jší, protože si tím připomínám, že se blok v>tve finally provede jako poslední. Ale z pohledu Pythonu je to jedno. Příklad:

print 'Start programu.'
try:
    try:
        data = file('data.dat')
        hodnota = int(data.readline().split()[2])
        print 'Hodnota je %d.' % (hodnota/(42-hodnota))
    except ZeroDivisionError: 
        print 'Hodnota byla 42.'
finally:
    data.close()
print 'Konec programu.'

V tomto případ> dojde k uzavření souboru vždy, nezávisle na tom, zda v bloku try/except vznikne výjimka. Všimn>te si odlišnosti chování ve vztahu k v>tvi else v konstrukci try/except/else, protože blok v else se zavolá jen v případ>, kdy nedojde k žádné výjimce. To by znamenalo, že by nedošlo k uzavření souboru. Pokud bychom zase kód pro uzavření souboru umístili jednoduše mimo konstrukci try/except, pak by se soubor neuzavřel v případ>, kdy by nastala jiná výjimka, než ZeroDivisionError. Takže pouze konstrukce try/finally zajistí, že k uzavření souboru dojde vždy.

Poznámka překladatele: V souboru data.dat se očekává alespoň jeden řádek, který obsahuje alespoň tři slova (řet>zce nerozd>lené mezerami) a třetí slovo má charakter čísla. Příklad obsahu:

xxx yyy 40

Zadáním hodnoty 42 (do souboru) vyvoláme výjimku ZeroDivisionError. Pokud místo čísla zadáme například řet>zec ccc, vznikne jiná výjimka (ValueError) související s tím, že se řet>zec nedaří převést na číslo. Pokud místo čísla neuvedeme vůbec nic, vznikne při následném volání metody split() k vygenerování kratšího seznamu, takže nebude existovat položka s indexem 2. V takovém případ> vznikne výjimka IndexError.

Osobn> se mi nelíbí otvírání souboru na jiné úrovni (v zanořeném try), než na jaké se provádí uzavírání — i když to funguje. Podle mého názoru by se soubor m>l zavírat na stejné úrovni v kódu, na které byl otevřen.

Platí to obecn>, ale nejvýrazn>ji je to vid>t v případ>, kdy jedna z akcí (otevření/zavření souboru) je umíst>na uvnitř funkce a druhá vn>. Například funkce, které dostává jako argument otevřený soubor, by jej nem>la zavírat. Ono to sice může fungovat naprosto bez problémů, ale může dojít ke zmatkům v naší hlav>, když si čteme zdrojový text. Důvod spočívá v tom, že při zápisu používání funkce je tato akce skryta našemu zraku a nemusíme si uv>domit, co se vevnitř d>je. Výjimku představují situace, kdy funkce svým jménem napovídá, že se předaný soubor uvnitř otevře, respektive uzavře.

Pokud tedy toto pravidlo selského rozumu napasujeme na výše uvedený příklad, pak osobn> dávám přednost následujícímu zápisu příkladu (navíc přidán příkaz print za uzavření souboru, abychom zviditelnili provedení bloku kódu):

print 'Start programu.'
try:
    data = file('data.dat')
    try:
        hodnota = int(data.readline().split()[2])
        print 'Hodnota je %d.' % (hodnota/(42-hodnota))
    except ZeroDivisionError: 
        print 'Hodnota byla 42.'
finally:
    data.close()
    print 'Soubor uzavřen.'
print 'Konec programu.'

Je tady ale ješt> jeden zádrhel. Pokud by navíc selhalo i otvírání zouboru data.dat, dojde k výjimce prom>nná data nebude napln>na (případn> se váže na předchozí hodnotu). To znamená, že v sekci finally, ve kterém se budeme pokoušet o volání metody close() objektu, který neexistuje. Proto je lepší zapsat:

print 'Start programu.'
data = file('data.dat')
try:
    try:
        hodnota = int(data.readline().split()[2])
        print 'Hodnota je %d.' % (hodnota/(42-hodnota))
    except ZeroDivisionError: 
        print 'Hodnota byla 42.'
finally:
    data.close()
    print 'Soubor uzavřen.'
print 'Konec programu.'

V takovém případ> je ovšem sporné už samotné použití try/finally, protože v případ>, kdy se soubor nepovede otevřít, nedostaneme se vůbec do následující konstrukce. Zavírání souboru stejn> v takovém případ> není možné (viz předchozí odstavec). Pokud se soubor povede otevřít, nepotřebujeme uzavírání souboru vkládat do bloku finally. Osobn> bych se proto přiklonil k naprosto jednoduchému řešení bez vn>jší konstrukce try/finally.

print 'Start programu.'
data = file('data.dat')

try:
    hodnota = int(data.readline().split()[2])
    print 'Hodnota je %d.' % (hodnota/(42-hodnota))
except ZeroDivisionError: 
    print 'Hodnota byla 42.'
except:
    print u'Nastala jiná výjimka.'
data.close()
print 'Konec programu.'

Záv>r: Původní příklad není tak jednoduchý, jak vypadá.

Generování chyb

Jakým způsobem můžeme generovat výjimky — dejme tomu uvnitř modulu —, které má zachytit n>kdo jiný? V jazyce Python je pro tento případ vyhrazeno klíčové slovo raise:

delenec = 42
delitel = input('Jakou hodnotou chcete d>lit číslo 42? ')
if delitel == 0:
   raise ZeroDivisionError()

Tento kód vygeneruje výjimku ZeroDivisionError, která může být zachycena v bloku try/except. Zbytku programu se to jeví naprosto stejn>, jako kdyby chybu vygeneroval přímo Python. Klíčovým slovem raise se předepisuje také předávání chyby zevnitř bloku except do vyšších úrovní programu. Při výskytu chyby můžeme například chtít provést n>jakou lokální akci, dejme tomu zapsat záznam do log souboru, ale poté chceme, aby se o dalších akcích rozhodlo na vyšších úrovních programu. Použití může vypadat takto:

logsoubor = file('errorlog.txt', 'w')

def f(hodnota):
    try:
        return 127 / (42-hodnota)
    except ZeroDivisionError:
        logsoubor.write('Hodnota byla 42.')
        raise

try:
    f(42)
except ZeroDivisionError:
    print 'Nastala chyba. Zkuste znovu.'

Povšimn>te si, jak funkce f() zachytává chybu, zapisuje zprávu do souboru se záznamem o chyb> a poté předává zachycenou výjimku ke zpracování v bloku kódu, který se nachází ve vn>jší, obalující konstrukci try/except.

Poznámka překladatele: S log soubory, tedy se soubory určenými pro záznam (protokolování) chyb a zvláštních stavů, se v>tšinou zachází tak, že se záznamy neustále připisují na konec. Prakticky to znamená, že by se log soubor m>l otvírat pro zápis za konec souboru, tedy v režimu append (druhý parametr s hodnotou 'a').

Další zásada vyplývá ze skutečnosti, že u otevřeného souboru není zaručeno, že je veškerý zapisovaný obsah skutečn> fyzicky uložen na disku. Pokud toho chceme dosáhnout, pak můžeme ve vhodných okamžicích provád>t takzvané vyprázdn>ní vyrovnávací pam>ti (flush).

Při zápisu do log souboru s chybami, kdy se předpokládá nízký počet zápisů, bývá praktičt>jší log soubor otevřít před každým zápisem a poté ho hned zavřít. Pokud aplikace havaruje, máme jistotu, že se neztratilo n>kolik posledních zápisů.

Ve výše uvedeném příklad> je log soubor otevřen na začátku a dokonce jsme jej zapom>li zavřít. Při výskytu chyby se původní obsah přepisuje, takže budeme mít zapsán jen poslední záznam. V mnoha jednoduchých případech to stačí, ale proč bychom to nemohli ud>lat pořádn>ji, když to není o moc složit>jší:

def f(hodnota):
    try:
        return 127 / (42-hodnota)
    except ZeroDivisionError:
        logsoubor = file('errorlog.txt', 'a') # Otevřeme pro připsání na konec,
        logsoubor.write('Hodnota byla 42.\n') # zapíšeme hodnotu      
        logsoubor.close()                     # a soubor uzavřeme.
        raise

try:
    f(42)
except ZeroDivisionError:
    print u'Nastalo d>lení nulou. Zkuste znovu.'

V praktických případech často používáme jediný log soubor a chceme do n>j zapisovat na více místech v programu. Museli bychom tedy na více místech opakovat výše uvedené okomentované řádky. Problém by nastal v situaci, kdy se rozhodneme například zm>nit jméno log souboru. V takovém případ> by nám m>lo v hlav> varovn> zasvítit pravidlo DRY z anglického Do not Repeat Yourself, čili voln> Neopakujte se. V>c jednoduše vyřešíme tím, že si pro zápis na konec log souboru vytvoříme vlastní funkci log(). S jejím využitím pak výsledek bude vypadat n>jak takto:

def log(zprava):
    logsoubor = file('errorlog.txt', 'a') # Otevřeme pro připsání na konec,
    logsoubor.write(zprava)               # zapíšeme hodnotu      
    logsoubor.close()                     # a soubor uzavřeme.

def f(hodnota):
    try:
        return 127 / (42-hodnota)
    except ZeroDivisionError:
        log('Hodnota byla 42.\n')  # Zápis zprávy do log souboru.
        raise

try:
    f(42)
except ZeroDivisionError:
    print u'Nastalo d>lení nulou. Zkuste znovu.'

V tomto okamžiku byste si mohli říci. Proč bych za každou zprávu nepřidával automaticky přechod na nový řádek přímo ve funkci log()? Nedoporučuji to. Uv>domte si, že v takovém případ> byste se zbavili možnosti zapisovat postupn> n>kolika voláními funkce více hodnot na jeden řádek log souboru. Místo toho je vhodn>jší vytvořit další, specializovan>jší funkci, která využívá výše definované, obecn>jší funkce log(). Speciální funkce může například do log souboru zapisovat i datum, čas a jmého přihlášeného uživatele:

def log(zprava):
    logsoubor = file('errorlog.txt', 'a') # Otevřeme pro připsání na konec,
    logsoubor.write(zprava)               # zapíšeme hodnotu      
    logsoubor.close()                     # a soubor uzavřeme.

def logErr(text):
    import time                           # Importujeme potřebné moduly.
    import getpass
    tim = time.strftime('%c')             # Získáme časovou značku.
    usr = getpass.getuser()               # Získáme jméno uživatele.
    log('%s %s: %s\n' % (tim, usr, text)) # Zapíšeme zformátovaný řádek.
    
def f(hodnota):
    try:
        return 127 / (42-hodnota)
    except ZeroDivisionError:
        logErr('Hodnota byla 42.')  # Zápis zprávy do log souboru.
        raise

try:
    f(42)
except ZeroDivisionError:
    print u'Nastalo d>lení nulou. Zkuste znovu.'

Za účelem zjemn>ní řízení našeho programu (nebo diagnostiky chyb) můžeme definovat své vlastní typy výjimek. Činíme tak prostřednictvím nových tříd výjimek. (S definicemi tříd jsme se krátce seznámili rámci tématu Data, datové typy a prom>nné a podrobn>ji se s nimi setkáme ješt> pozd>ji v kapitole v>nované objektov> orientovanému programování.) Pro tento účel obvykle definujeme třídu velmi prostou, která nedefinuje žádný další obsah a která je pouze odvozena od standardní bázové třídy Exception. Používá se jako chytré náv>ští, které se dá rozpoznávat v příkazech except. Spokojme se s následujícím stručným příkladem:

class BrokenError(Exception): pass

try:
   raise BrokenError
except BrokenError:
   print u'Narazili jsme na chybu při zpracování.'

Poznámka překladatele: Vzhledem k následující autorov> poznámce jsem ponechal původní anglický identifikátor. Připojuji se k výzv> dodržovat níže uvedenou konvenci.

Povšimn>te si, že jsme při tvorb> jména použili konvenci, kdy se na konec jména třídy dává přípona "Error" (čili chyba). Povšimn>te si, že d>díme chování obecné třídy Exception ([iksepšn], výjimka) tím, že její jméno uvedeme do závorek za jménem definované třídy. K d>dičnosti se podrobn>ji dostaneme v kapitole v>nované objektov> orientovanému programování.

Ješt> poslední poznámka k části týkající se generování chyb. Prozatím jsme pro předčasné ukončování našich programů provád>li importováním modulu sys a voláním jeho funkce exit(). Jiný způsob, kterým dosáhneme naprosto stejného výsledku, spočívá ve vyvolání výjimky SystemExit:

>>> raise SystemExit

Hlavní výhoda tohoto přístupu spočívá v tom, že nemusíme nejdříve provést import sys.

Poznámka překladatele: Osobn> se k tomuto postupu moc nepřikláním. Volání funkce exit() je známé i z jiných jazyků a jiným čtenářům vašeho zdrojového textu může volání sys.exit() připadat přirozen>jší.

JavaScript

V jazyce JavaScript se zpracování chyb provádí velmi podobn>, jako v jazyce Python. Jen místo pythonovských klíčových slov try, except a raise se používají klíčová slova try, catch ([keč], chytit) a throw (vrhnout, hodit).

Ukážeme si pár příkladů, ale principy zůstávají naprosto stejné jako v jazyce Python. V jazyce JavaScript ale nemáme konstrukci try/finally.

Zachytávání chyb

Zachytávání chyb v bloku kódu se předepisuje klíčovým slovem try a sadou příkazů catch tém>ř stejn>, jako v Pythonu:

<script type="text/javascript">
try {
    var x = NeexistujiciFunkce();
    document.write(x);
}
catch(err) {
    document.write("Došlo k chyb>.");
}
</script>

Vyvolání chyby

V jazyce Python jsme k vyvolání chyby používali klíčové slovo raise. V jazyce JavaScript používáme podobným způsobem throw(). Také v jazyce JavaScript si můžeme vytvořit vlastní typy chyb, jako v Pythonu. Ale mnohem jednodušší způsob spočívá v použití řet>zce.

<script type="text/javascript">
try {
    throw("Nová chyba");
}
catch(e) {
    if (e == "Nová chyba")
        document.write("Zachytili jsme novou chybu.");
    else
        document.write("Nenastala nová chyba.");
}
</script>

Poznámka překladatele: Řet>zce se pro výjimky používaly dříve i v jazyce Python. Z důvodů zp>tné kompatibility jsou dosud podporovány, ale u nových programů se jejich používání nedoporučuje. Do budoucna se plánuje odstran>ní této možnosti.

To je vše, co si o zpracování chyb řekneme. V tématech pro pokročilé uvidíte použití mechanismu pro zpracování chyb v praxi, spolu s použitím dalších základních konceptů, jako jsou posloupnosti, cykly a v>tvení. V tomto okamžiku již máte k dispozici všechny podstatné nástroje, které potřebujete pro vytváření mocných programů. Možná byste si teď m>li zkusit n>jaké vlastní programy vytvořit. Stačí pár, jen abyste dostali popisované mechanismy do hlavy před tím, než se pustíme do dalších témat. Pár nám>tů:

Abyste se s výše uvedenými úkoly vyrovnali, budete muset použít všechny rysy jazyka, se kterými jsme se dosud seznámili, a možná i pár dodávaných modulů. Nezapomeňte občas nahlédnout do dokumentace. Pravd>podobn> v ní najdete pár v>cí, které vám ulehčí práci. Nezapomínejte taky na užitečnost interaktivního režimu (>>>). Zkoušejte si v n>m nové v>ci, dokud nepochopíte, jak fungují. Teprve poté přeneste získané znalosti do vašeho programu. Tímto způsobem pracují i profesionálové. A co je taky důležité, dobře se bavte!

Nashledanou v části pro pokročilé :-)

Zapamatujte si

Pokud vás napadne, co by se dalo na překladu této kapitoly vylepšit, zašlete e-mail odklepnutím Tím budou do dopisu automaticky vloženy informace o tomto HTML dokumentu.

$Id: cztuterrors.html,v 1.8 2005/10/20 20:55:24 petr Exp $