Node

Do sada smo naučili kako se JavaScript jezik koristi u brauzerima. U ovom poglavlju ćemo se upoznati sa Node.js programom koji nam omogućava da znanje JavaScript-a primenimo van brauzera. Uz pomoć Node.js možemo da razvijamo programe na raznim nivoima, od prostih programa do HTTP servera, odnosno web aplikacija.

Sinhroni i asinhroni I/O

Jedan od težih problema u programiranju aplikacija sa kojima se komunicira preko mreže je kako upravljati ulazom i izlazom, odnosno kako čitati (read) i pisati (write) podatke sa mreže, diskova i drugih uređaja. Prenos podataka kroz mrežu zahteva vreme pa se postavlja pitanje na koji način postići potrebnu brzinu rada takvih aplikacija.

Tradicionalan način upravljanja ulazom i izlazom je da imamo funkciju kao što je readFile koja počinje čitanje i vraća podatke tek kada fajl bude ceo pročitan. To se naziva sinhronim I/O operacijama (I/O je skraćenica za input/output).

Node je prvobitno bio namenjen da se lako ostavri asinhroni I/O. Mi smo se sa asinhronim interfejsom sreli ranije kada somo koristili XMLHttpRequest ( Ajax call ). Asinhroni interfejs omogućava JavaScript programu da nastavi izvršavanje bez zaustavljanja i da pozove povratnu (callback) funkciju kada interfejs završi rad. Upravo tako Node vrši I/O operacija. Node je nastao 2009 godine, kada je Ajax već uveliko bio korišćen tako da su programeri bili spremni za rad sa asinhronim I/O.

Node komande

Kada je Node.js instaliran (Uputstvo za instalaciju ), onda nam je na raspolaganju program node kojim startujemo JavaScript fajl. Na primer ako imamo fajl hello.js u kojem se nalaze sledeće JavaScript naredbe:

var message = "Hello world";
console.log(message);

onda komandom: node hello.js izvršavamo gornji program i dobijamo odgovor:

Hello world

Ako ukucate naredbu node ( bez imena fajla ), time ćete aktivirati interpreter, koji vam omogućava da unosite JavaScript naredbe koje se odmah izvršavaju.

Evo primera:

$ node
> 1 + 1
2
> [-1, -2, -3].map(Math.abs)
[1, 2, 3]
> process.exit(0)
$

Moduli

Sve funkcionalnost Node-a se nalazi u modulima. Moduli su JavaScript fajlovi koji sadrže funkcionalnost. Node ima bogatu bilblioteku ugrađenih (built-in) modula. Vi možete napisati i dodavati svoje module. Listu najpopularnijih modula možete da pogledate ako kliknete ovde

NPM je on-line repozitorijum JavaScript modula, od kojih su mnogi pisani upravo za Node. Kada intslirate Node na vašem kompjuteru, uz instalaciju dobiete program npm koji predstavlja interfejs prema NPM repozitorijumu ( pomoću njega možete da iz repozitorijuma preuzimate module ).

Na primer, jedan modul koji se nalazi u NPM-u je modul figlet, koji konvertuje ASCII slova u artistički tekst. Sledeće komande pokazuju kako možete da instalirate i iskoristite ovaj modul u vašem programu:

$ npm install figlet
npm GET https://registry.npmjs.org/figlet
npm 200 https://registry.npmjs.org/figlet
npm GET https://registry.npmjs.org/figlet/-/figlet-1.0.9.tgz
npm 200 https://registry.npmjs.org/figlet/-/figlet-1.0.9.tgz
figlet@1.0.9 node_modules/figlet
$ node
> var figlet = require("figlet");
> figlet.text("Hello world!", function(error, data) {
          if (error)
            console.error(error);
          else
            console.log(data);
        });

Rezutlat rada gornjeg programa je:

 _   _      _ _                            _     _ _
| | | | ___| | | ___   __      _____  _ __| | __| | |
| |_| |/ _ \ | |/ _ \  \ \ /\ / / _ \| '__| |/ _` | |
|  _  |  __/ | | (_) |  \ V  V / (_) | |  | | (_| |_|
|_| |_|\___|_|_|\___/    \_/\_/ \___/|_|  |_|\__,_(_)

Nakon izvršavanja npm install figlet komande, NPM će na vašem računaru kreirati folder pod imenom node_modules. U tom direktoriju naći će se folder figlet koji sadrži biblioteku. Kada damo komandu require(“figlet”) ova biblioteka biće učitana pa mi možemo da pozovemo njenu metodu text da nacrtamo velika slova kako je gore prikazano.

Možda je malo neočekivano da metoda text prosto ne vraća string sa velikim slovima, već da kao drugi argument koristi povratnu funkciju kojoj vraća rezultat. Ali ne samo rezultat već i grešku koja bi mogla da nastane tokom procesiranja.

Ovo je zapravo standardan način kako Node funkcioniše. Da bi metoda text bila izvršena potrebno je da sa diska učita oblike slova kojima će da zameni ASCII slova, a to zahteva vreme, pa metod ne može odmah da vrati rezultat. Ali tu dolazi do izražaja asinhronizam, je metod odmah završava rad i JavaScript može da ide dalje, a kad metod završi posao biće pozvana povratna funkcija da iskoristi rezultat.

Komanda npm install radi još neke stvari osim što instalira modul. Ako je prisutan fajl package.json, npm čita taj fajl u kojem se nalaze međuzavisnoti traženog modula i drugih modula. Svi ti moduli će biti učitani jednom npm install komandom.

Ovde nemamo ni vremena ni prostora da se bavimo svim detaljima NPM-a. Sa svim detaljima možete se upoznati na nmpjs.org

Modul za rad sa fajlovima

Jedan od najviše korišćenih built-in je “fs” modul (fs je skraćenica za file system). Ovaj modul sadrži funkcije za rad sa fajlovim i folderima (direktorijumima).

Na primer, u “fs” modulu postoji funkcija readFile koja čita sadržaj fajla i poziva povratnu (callback) funkciju sa učitanim sadržajem kao argumentom.

var fs = require("fs");
fs.readFile("file.txt", "utf8", function(error, text) {
        if (error)
          throw error;
        console.log("The file contained:", text);
});

Drugi argument u readFile metodi označava koji kodni sistem treba primeniti da se sadržaj fajla konvertuje u string. Postoji više kodnih sistema, ali se danas najčešće primenjuje UTF-8 za kodiranje teksta. Ako se ovaj argument ne navede u pozivu readFile metode, podrazumeva se da je sadržaj fajla u binarnom obliku i da ćete čitanjem fajla dobiti Buffer objekat umesto stringa. Buffer objekat je sličan nizu (array) bajtova (bytes) koji su smešteni u fajl.

Evo primera sa fajlom koji sadrži sliku ( slike su nizovi bajtova, a ne stringovi ):

var fs = require("fs");
fs.readFile("slika.jpg", function(error, buffer) {
        if (error)
          throw error;
        console.log("The file contained", buffer.length, "bytes.",
                    "The first byte is:", buffer[0]);
});

Slična funkcija, writeFile, postoji i za upisivanje fajlova na disk.

Primer:

var fs = require("fs");
fs.writeFile("graffiti.txt", "Node was here", function(err) {
        if (err)
          console.log("Failed to write file:", err);
        else
          console.log("File written.");
});

Ovde nije potrebno da navodite kodni sistem pošto će writeFile podrazumevati da ako ste mu dali string, a ne Buffer objekat, onda če string po difoltu biti kodiran kao UTF-8.

Moful “fs” sadrži i mnoge druge korisne funkcije; readdir vraća fajlove u folderu, stat vraća informacije o fajlu, rename menja ime fajla, unlink briše fajl itd. Detaljniji opis fs modula možete da pronađete na node.js sajtu.

Večina funkcija iz fs modula ima i sinhronu varijantu. Na primer postoji sinhrona varijanta za readFile koja se naziva readFileSync.

Evo primera:

var fs = require("fs");
console.log(fs.readFileSync("file.txt", "utf8"));

Sinhrone varijante su jednostavnije za pozivanje, ali treba imati na umu da kada se vrši sinhrona operacija vaš program se zaustavlja sve dok se operacija ne završi, što može imati posledice u sporom odgovoru na zahteve korisnika.

Detaljan opis fs modula možete da pogledate u Node File System

HTTP modul

Drugi, centralni modul Node platforme je “http” modul, koji daje potrebnu funkcionalnost za kreiranje HTTP servera i postavljanje HTTP zahteva.

Evo kako se pomoću “http” modula kreira i startuje jedan jednostavan HTTP server:

var http = require("http");
var server = http.createServer(function(request, response) {
        response.writeHead(200, {"Content-Type": "text/html"});
        response.write("<h1>Hello!</h1><p>You asked for <code>" +
                       request.url + "</code></p>");
        response.end();
});
server.listen(8000);

Nakon startovanja ovog programa na vašem računaru, možete brauzerom na adresi http://localhost:8000/hello dobiti odgovor od servera u obliku male HTML stranice.

Funkcija koja je preneta kao argument u createServer metodi poziva se svaki put kada se klijent (brauzer) poveže na server. Varijable request i response su objekti koji predtstavljaju pdatke koje server prima od klijenta (request) i šalje klijentu (responese). Objekat request sadrži informacije o zahtevu klijenta kao što su na primer url koji nam kazuje koji URL klijent zahteva.

Da nešto vratite nazad klijentu, pozivate metode koje su deo response objekta. Prva metoda, writeHead, šalje takozvane hedere. U njima šaljete klijentu statusni kod ( 200 za “OK” u gornjem slučaju) kao i objekat koji sadrži vrednost hedera. U gornjem slučaju rekli ste klijentu da ćemo mu poslati HTML dokument.

Kao sledeće, metodom response.write šaljemo sam dokument. Ovaj metod možete pozivati više puta kada želite da odgovor bude iz više delova. Na kraju pozivom response.end signalizirate da ste završili sa odgovorom klijentu.

Poziv server.listen uzrokuje da server počne sa konekcijama na portu 8000. Zato vi u brauzeru morate da navedete localhost:8000, a ne samo localhost ako želite da komunicirate sa ovim serverom (Napomena: localhost bez navođenja porta podrazumeva port 80).

Za zaustavljenje Node programa kao što je ovaj serverski program, koji neprekidno čeka nove konekcije od strane klijenata, koristimo Ctrl-C (istovremeni pritisak na Crtl i C tipke na tastaturi).

Stvarni web serveri rade mnogo više od ovog prikazanog u gornjem primeru. Oni analiziraju objekat request da “shvate” kakvu uslugu klijent traži i koji URL resurs ta usluga zahteva.

Modulom “http” možemo kreirati i zahtev prema serveru (umesto brauzera)

Primer:

var http = require("http");
var request = http.request({
        hostname: "eloquentjavascript.net",
        path: "/20_node.html",
        method: "GET",
        headers: {Accept: "text/html"}
}, function(response) {
        console.log("Server responded with status code",
                    response.statusCode);
});
request.end();

Prvi argument konfiguriše zahtev, kazujući kojem se serveru obraćamo, koju putanju resursa tražimo, koji metod korsitimo itd. Drugi argument je funkcija koja će biti pozvana kada stigne odgovor od servera. Ona dobija kao argument jedan objekat (responese) koji sadrži sve elemente odgovora od strane servera ( statusni kod itd.)

Kao i kod responese objekta koji smo videli kod servera, request objekat nam omogućava da serveru saljemo podatke write metodom i da na kraju end metodom završimo slanje. U gornjem primeru nismo koristili write metodu, jer GET zahtevom se ne šalju podaci ( to se radi POST zahtevom, na primer).

Da zahtev učinimo bezbednim, Node nam stavlja na raspolaganje paket https koji sadrži svoju request metodu.

Detaljan opis http modula možete da nađete u Node HTTP modul

O Node.js napisano je mnogo knjiga, tutorijala, vodiča i ostale literature.

Ovde smo izdvojili dva izvora za dodatno učenje o Node platformi:

Uz već pomenuti pretraživač Node modula : Node moduli ovo bi trebalo da predstavlja dovoljnu informaciju za rad sa Node platformom.

Napominjemo takođe, da za razvoj web aplikacija postoji jedan veoma koristan Node frejmvork pod nazivom express. O njemu možete više da saznate na sajtu Node Express Korišćenjem frejmovrka, kao što je express, može se značajno olakšati razvoj i održavanje velikih web aplikacija.

S obzirom na jednostavnost vaših projekata preporučujemo da se oslonite na izvorni Node.js, bez korišćenja frejmvorka.

Jedan širi skup resursa za izučavanje Node.js možete da nađete u How Do I Get Started With Node.js