Jak Node.js funguje
Tato lekce vysvětluje, jak Node.js zpracovává příchozí úlohy do modulu runtime JavaScriptu.
Typy úkolů
JavaScriptové aplikace mají dva typy úloh:
- Synchronní úlohy: K těmto úkolům dochází v pořadí. Nejsou závislé na dokončení jiného prostředku. Příklady jsou matematické operace nebo manipulace s řetězci.
- Asynchronní: Tyto úlohy nemusí být dokončeny okamžitě, protože jsou závislé na jiných prostředcích. Příkladem jsou síťové požadavky nebo operace systému souborů.
Vzhledem k tomu, že chcete, aby program běžel co nejrychleji, chcete, aby javascriptový modul mohl pokračovat v práci, zatímco čeká na odpověď z asynchronní operace. Aby to bylo možné provést, přidá asynchronní úlohu do fronty úloh a pokračuje v práci na další úloze.
Správa fronty úloh pomocí smyčky událostí
Node.js používá architekturu řízenou událostmi modulu JavaScriptu ke zpracování asynchronních požadavků. Následující diagram znázorňuje, jak funguje smyčka událostí V8 na vysoké úrovni:
Asynchronní úloha označená odpovídající syntaxí (viz níže) se přidá do smyčky událostí. Úkol zahrnuje práci, kterou chcete provést, a funkci zpětného volání pro příjem výsledků. Po dokončení náročné operace se funkce zpětného volání aktivuje s výsledky.
Synchronní operace versus asynchronní operace
Rozhraní API Node.js poskytují asynchronní i synchronní operace pro některé ze stejných operací, jako jsou operace se soubory. Obecně byste měli vždy myslet na asynchronní-první, existují časy, kdy byste mohli použít synchronní operace.
Příkladem je, když rozhraní příkazového řádku (CLI) přečte soubor a okamžitě použije data v souboru. V tomto případě můžete použít synchronní verzi operace se souborem, protože neexistuje žádný jiný systém nebo osoba, která čeká na použití aplikace.
Pokud ale vytváříte webový server, měli byste vždy použít asynchronní verzi operace se souborem, abyste nezablokovali možnost provádění jednoho vlákna ke zpracování jiných uživatelských požadavků.
Ve své práci jako vývojář ve firmě TailWind Traders budete muset porozumět rozdílu mezi synchronními a asynchronními operacemi a kdy je použít.
Výkon prostřednictvím asynchronních operací
Node.js využívá jedinečnou povahu JavaScriptu řízenou událostmi, díky čemuž je psaní úloh serveru rychlé a vysoce výkonné. JavaScript, pokud se správně používá s asynchronními technikami, může produkovat stejné výsledky výkonu jako jazyky nízké úrovně, jako je C, protože modul V8 umožnil zvýšení výkonu.
Asynchronní techniky mají tři styly, které je potřeba rozpoznat ve své práci:
- Async/await (doporučeno): Nejnovější asynchronní technika, která používá klíčová slova a
await
používáasync
k získání výsledků asynchronní operace. Asynchronní/await se používá v mnoha programovacích jazycích. Obecně platí, že nové projekty s novějšími závislostmi budou používat tento styl asynchronního kódu. - Zpětná volání: Původní asynchronní technika, která používá funkci zpětného volání k získání výsledků asynchronní operace. Uvidíte to ve starších základech kódu a ve starších rozhraních API Node.js.
- Přísliby: Novější asynchronní technika, která používá objekt příslibu k získání výsledků asynchronní operace. Uvidíte to v novějších základech kódu a v novějších rozhraních API Node.js. Možná budete muset do své práce napsat kód založený na příslibu, abyste zabalili starší rozhraní API, která nebudou aktualizována. Pomocí příslibů pro toto zabalení povolíte použití kódu ve větším rozsahu Node.js projektů s verzí než v novějším stylu async/await kódu.
Asynchronní/await
Asynchronní/await je nejnovější způsob, jak zpracovat asynchronní programování. Asynchronní/await je syntaktický cukr nad přísliby a dělá asynchronní kód spíše jako synchronní kód. Je také jednodušší číst a udržovat.
Stejný příklad použití async/await vypadá takto:
// async/await asynchronous example
const fs = require('fs').promises;
const filePath = './file.txt';
// `async` before the parent function
async function readFileAsync() {
try {
// `await` before the async method
const data = await fs.readFile(filePath, 'utf-8');
console.log(data);
console.log('Done!');
} catch (error) {
console.log('An error occurred...: ', error);
}
}
readFileAsync()
.then(() => {
console.log('Success!');
})
.catch((error) => {
console.log('An error occurred...: ', error);
});
Když byla v ES2017 vydána asynchronní/await, dají se klíčová slova použít jenom ve funkcích s funkcí nejvyšší úrovně, která představuje příslib. I když příslib nemusel mít then
a catch
oddíly, bylo stále nutné, aby se promise
spustila syntaxe.
Funkce async
vždy vrátí příslib, i když nemá await
uvnitř volání. Příslib se vyřeší s hodnotou vrácenou funkcí. Pokud funkce vyvolá chybu, příslib bude odmítnut s vyvolanou hodnotou.
Sliby
Vzhledem k tomu, že zpětné volání vnořených volání může být obtížné číst a spravovat, Node.js přidání podpory pro přísliby. Příslib je objekt, který představuje konečné dokončení (nebo selhání) asynchronní operace.
Funkce příslibu má formát:
// Create a basic promise function
function promiseFunction() {
return new Promise((resolve, reject) => {
// do something
if (error) {
// indicate success
reject(error);
} else {
// indicate error
resolve(data);
}
});
}
// Call a basic promise function
promiseFunction()
.then((data) => {
// handle success
})
.catch((error) => {
// handle error
});
Metoda then
je volána, když je příslib splněn a catch
metoda je volána při odmítnutí příslibu.
Pokud chcete soubor číst asynchronně s přísliby, je tento kód následující:
// promises asynchronous example
const fs = require('fs').promises;
const filePath = './file.txt';
// request to read a file
fs.readFile(filePath, 'utf-8')
.then((data) => {
console.log(data);
console.log('Done!');
})
.catch((error) => {
console.log('An error occurred...: ', error);
});
console.log(`I'm the last line of the file!`);
Asynchronní/await nejvyšší úrovně
Nejnovější verze Node.js přidali asynchronní/await nejvyšší úrovně pro moduly ES6. Musíte přidat vlastnost pojmenovanou type
v package.json s hodnotou, která module
má tuto funkci použít.
{
"type": "module"
}
Pak můžete klíčové slovo použít await
na nejvyšší úrovni kódu.
// top-level async/await asynchronous example
const fs = require('fs').promises;
const filePath = './file.txt';
// `async` before the parent function
try {
// `await` before the async method
const data = await fs.readFile(filePath, 'utf-8');
console.log(data);
console.log('Done!');
} catch (error) {
console.log('An error occurred...: ', error);
}
console.log("I'm the last line of the file!");
Zpětná volání
Když byl původně vydán Node.js, asynchronní programování bylo zpracováno pomocí funkcí zpětného volání. Zpětná volání jsou funkce, které se předávají jako argumenty jiným funkcím. Po dokončení úkolu se volá funkce zpětného volání.
Pořadí parametrů funkce je důležité. Funkce zpětného volání je posledním parametrem funkce.
// Callback function is the last parameter
function(param1, param2, paramN, callback)
Název funkce v kódu, který udržujete, nemusí být volána callback
. Může být volána cb
nebo done
next
. Název funkce není důležitý, ale pořadí parametrů je důležité.
Všimněte si, že funkce není syntaktická indikace , že je funkce asynchronní. Musíte vědět, že funkce je asynchronní, a to čtením dokumentace nebo pokračováním v čtení kódu.
Příklad zpětného volání s pojmenovanou funkcí zpětného volání
Následující kód odděluje asynchronní funkci od zpětného volání. Je to snadné čtení a pochopení a umožňuje opakovaně používat zpětné volání pro další asynchronní funkce.
// callback asynchronous example
// file system module from Node.js
const fs = require('fs');
// relative path to file
const filePath = './file.txt';
// callback
const callback = (error, data) => {
if (error) {
console.log('An error occurred...: ', error);
} else {
console.log(data); // Hi, developers!
console.log('Done!');
}
};
// async request to read a file
//
// parameter 1: filePath
// parameter 2: encoding of utf-8
// parmeter 3: callback function
fs.readFile(filePath, 'utf-8', callback);
console.log("I'm the last line of the file!");
Správný výsledek je:
I'm the last line of the file!
Hi, developers!
Done!
Nejprve se spustí asynchronní funkce fs.readFile
a přejde do smyčky událostí. Pak provádění kódu pokračuje na další řádek kódu, což je poslední console.log
. Po přečtení souboru se volá funkce zpětného volání a spustí se dva příkazy console.log.
Příklad zpětného volání s anonymní funkcí
Následující příklad používá anonymní funkci zpětného volání, což znamená, že funkce nemá název a nelze ji znovu použít jinými anonymními funkcemi.
// callback asynchronous example
// file system module from Node.js
const fs = require('fs');
// relative path to file
const filePath = './file.txt';
// async request to read a file
//
// parameter 1: filePath
// parameter 2: encoding of utf-8
// parmeter 3: callback function () => {}
fs.readFile(filePath, 'utf-8', (error, data) => {
if (error) {
console.log('An error occurred...: ', error);
} else {
console.log(data); // Hi, developers!
console.log('Done!');
}
});
console.log("I'm the last line of the file!");
Správný výsledek je:
I'm the last line of the file!
Hi, developers!
Done!
Při spuštění kódu se spustí asynchronní funkce fs.readFile
a přejde do smyčky událostí. Dále provádění pokračuje na následující řádek kódu, což je poslední console.log
. Při čtení souboru se volá funkce zpětného volání a spustí se dva příkazy console.log.
Vnořené zpětná volání
Vzhledem k tomu, že možná budete muset volat následné asynchronní zpětné volání a pak jiný, může se kód zpětného volání vnořit. To se nazývá zpětné volání peklo a je obtížné číst a udržovat.
// nested callback example
// file system module from Node.js
const fs = require('fs');
fs.readFile(param1, param2, (error, data) => {
if (!error) {
fs.writeFile(paramsWrite, (error, data) => {
if (!error) {
fs.readFile(paramsRead, (error, data) => {
if (!error) {
// do something
}
});
}
});
}
});
Synchronní rozhraní API
Node.js má také sadu synchronních rozhraní API. Tato rozhraní API blokují provádění programu, dokud se úkol neskončí. Synchronní rozhraní API jsou užitečná, když chcete přečíst soubor a pak okamžitě použít data v souboru.
Synchronní (blokující) funkce v Node.js používají konvenci pojmenování .functionSync
Asynchronní rozhraní API má například readFile
synchronní protějšek s názvem readFileSync
. Je důležité dodržovat tento standard ve vlastních projektech, aby byl kód snadno čitelný a srozumitelný.
// synchronous example
const fs = require('fs');
const filePath = './file.txt';
try {
// request to read a file
const data = fs.readFileSync(filePath, 'utf-8');
console.log(data);
console.log('Done!');
} catch (error) {
console.log('An error occurred...: ', error);
}
Jako nový vývojář ve společnosti TailWind Traders můžete být požádáni o úpravu libovolného typu kódu Node.js. Je důležité pochopit rozdíl mezi synchronními a asynchronními rozhraními API a různými syntaxemi asynchronního kódu.