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 $