Node.JS 6: Asynchronní a synchronní operace se soubory
V minulé epizodě jsme se podívali na základní funkčnost fs modulu, dnes se ponoříme do hlubšího porozumění rozdílů mezi asynchronními a synchronními metodami, které zpracovávají soubory. Asynchronní a synchronní přístup bývá často označován i jako blokující (blocking) a neblokující (non-blocking) kód.
Blokující (blocking) a neblokující (non-blocking) způsob vykonávání kódu
V případě blokujícího kódu dochází ke spuštění Javascriptu až poté, co jsou hotové nejavascriptové (non-Javascript) operace. Děje se to díky event loop (událostní smyčce), která není schopná pokračovat v Javascriptovém kódu a zároveň vykonávat blokující (blocking) operaci. Nejčastěji stojí za blokujícími operacemi V/V (angl. I/O) metody.
Princip V/V (I/O) - I/O představuje zkratku pro Input/Output, česky Vstup/Výstup. V/V (I/O) systém přenáší informace mezi hlavní pamětí počítače a vnějším světem. , sám se sestává z V/V (I/O) zařízení (periferních - např. myš, klávesnice nebo webkamera), V/V (I/O)řadičem (CU - Central unit, tj. elektronickou řídící jednotkou, která vede činnost všech částí počítače a je součástí procesoru (CPU)) a V/V (I/O) softwaru, který vykoná V/V (I/O) transakci pomocí sekvence V/V (I/O) operací - a obsahuje i již zmíněné V/V (I/O) metody.
Blokující metody vykonávají kód synchronně, neblokující asynchronně.
Rozdíly v běžném kódu
Jaké odlišnosti pro nás představuje toto rozdělení v samotném skriptu? Uveďme si příklad již známé metody fs.readFile():
Synchronní verze
const fs = require('fs');
fs.writeFileSync('soubor.txt', 'Dobrý den!');
if (fs.existsSync('soubor.txt')) {
console.log("Uloženo!")
}
const data = fs.readFileSync('soubor.txt');
console.log(data.toString())
function dalsiUloha() {
console.log("Další úloha.");
}
dalsiUloha();
Výpis v konzoli:
Uloženo!
Dobrý den!
Další úloha.
Abychom zjistili, zda-li je již vytvořen soubor.txt, použijeme další synchronní funkci fs.existsSync (pokud soubor existuje, vykoná se podmínka - v našem případě se vypíše Uloženo!, ovšem jestliže se soubor metodou fs.readFileSync ještě nevytvořil, příkaz Uloženo! se nevypíše.
Asynchronní verze
const fs = require('fs');
fs.writeFile('soubor.txt', 'Dobrý den!', function(err){
if (err) throw err;
console.log('Uloženo!');
});
fs.readFile('soubor.txt', function (err, data) {
if (err) throw err;
console.log(data.toString())
});
function dalsiUloha() {
console.log("Další úloha.");
}
dalsiUloha();
Výpis v konzoli:
Další úloha.
Uloženo!
Dobrý den!
V prvním případě voláme console.log před dalsiUloha(), v druhém případě (neblokujícím) může vykonávání javascriptového kódu pokračovat tak, že dalsiUloha() se začne vykonávat jako první. Schopnost spustit další úlohy aniž bychom museli čekat na přečtení souboru k dokončení prvního úkolu zadání patří mezi klíčové kroky umožňující vyšší průtok informací rychleji.
Kombinování blokujícího a neblokujícího kódu občas nese neblahé následky - např. pokud se rozhodneme přečíst soubor asynchronně a následně jej synchronně odstranit, může dojít nejprve k jeho vymazání a potom k pokusu jej přečíst:
const fs = require('fs');
fs.readFile('soubor.txt', function (err, data){
if (err) throw err;
console.log(data.toString());
});
fs.unlinkSync('soubor.txt');
Pokud bude soubor.txt prázdný, fs.readFile jej přečte velmi rychle a k chybovému hlášení nedojde.
Zcela asynchronní verze by mohla vypadat například takto:
const fs = require('fs');
fs.readFile('soubor.txt', function (err, data){
if (err) throw err;
console.log(data.toString());
fs.unlink('soubor.txt', function (err, data) {
if (err) throw err;
});
});
Tento kód využívá vnoření fs.unlink do callback funkce ( = funkce volající další funkci) fs.readFile, díky čemuž máme garantované správné pořadí operací.