Node.js: kaj je to, kdaj in kako ga uporabljati ter zakaj

Verjetno ste že prebrali te stavke ...

Node.js je izvajalno okolje JavaScript, zgrajeno na Chromovem V8 JavaScript engineNode.js uporablja asinhroni ne-blokirni V / I model, ki temelji na dogodkih, in deluje na zanki z enim niti

... in se spraševali, kaj vse to pomeni. Upamo, da boste do konca tega članka bolje razumeli te izraze in tudi, kaj je Node, kako deluje in zakaj in kdaj je dobra ideja, da ga uporabite.

Začnimo s pregledom terminologije.

V / I (vhod / izhod)

Kratko za vhod / izhod se I / O nanaša predvsem na interakcijo programa s sistemskim diskom in omrežjem. Primeri I / O operacij vključujejo branje / zapisovanje podatkov z / na disk, podajanje zahtev HTTP in pogovor z bazami podatkov. V primerjavi z dostopom do pomnilnika (RAM) ali delom na CPU so zelo počasni.

Sinhrono vs Asinhrono

Sinhrono (ali sinhronizirano) izvajanje se običajno nanaša na zaporedno izvajanje kode. Pri sinhronizacijskem programiranju se program izvaja vrstica za vrstico, ena za drugo. Vsakič, ko pokličete funkcijo, izvajanje programa počaka, dokler se ta funkcija ne vrne, preden nadaljuje v naslednjo vrstico kode.

Asinhrono (ali asinhrono) izvajanje se nanaša na izvajanje, ki se ne izvaja v zaporedju, kot je prikazano v kodi. Pri asinhronem programiranju program ne čaka, da se naloga konča, in lahko preide na naslednjo nalogo.

V naslednjem primeru sinhronizacija povzroči, da se opozorila sprožijo zaporedoma. Med asinhronizacijo se opozorilo (2), čeprav se zdi, da se izvede drugo, ne.

// Synchronous: 1,2,3 alert(1); alert(2); alert(3); // Asynchronous: 1,3,2 alert(1); setTimeout(() => alert(2), 0); alert(3);

Asinhronska operacija je pogosto povezana z V / I, čeprav setTimeoutje primer nečesa, kar ni V / I, a še vedno asinhronizirano. Na splošno je vse, kar je povezano z računi, sinhronizacija in vse, kar je povezano z vhodom / izhodom / časom, je asinhronizacijsko. Razlog za to, da se V / I operacije izvajajo asinhrono, je, da so zelo počasne in bi v nasprotnem primeru blokirale nadaljnje izvajanje kode.

Blokiranje in neblokiranje

Blokiranje se nanaša na operacije, ki blokirajo nadaljnje izvajanje, dokler se ta operacija ne konča, neblokiranje pa na kodo, ki ne blokira izvajanja. Ali kot pravi dokument Node.js, je blokiranje takrat, ko mora izvajanje dodatnega JavaScript-a v procesu Node.js počakati, dokler se operacija, ki ni JavaScript, konča.

Blokirne metode se izvajajo sinhrono, neblokirajoče pa asinhrono.

// Blocking const fs = require('fs'); const data = fs.readFileSync('/file.md'); // blocks here until file is read console.log(data); moreWork(); // will run after console.log // Non-blocking const fs = require('fs'); fs.readFile('/file.md', (err, data) => { if (err) throw err; console.log(data); }); moreWork(); // will run before console.log

V prvem primeru zgoraj console.logbomo poklicali prej moreWork(). V drugem primeru fs.readFile()ne blokira, zato se lahko izvajanje JavaScript nadaljuje in moreWork()bo poklicano prvo.

V Node se neblokiranje nanaša predvsem na V / I operacije, JavaScript, ki kaže slabo zmogljivost, ker je CPU intenziven in ne čaka na operacijo, ki ni JavaScript, kot je V / I, običajno ne imenujemo blokiranje.

Vse metode V / I v standardni knjižnici Node.js nudijo asinhrične različice, ki ne blokirajo, in sprejemajo funkcije povratnega klica. Nekatere metode imajo tudi blokade, ki se končajo s sinhronizacijo.

Neblokirajoče V / I operacije omogočajo, da en sam proces hkrati postreže z več zahtevami. Namesto da se postopek blokira in čaka, da se V / I operacije zaključijo, se V / I operacije prenesejo v sistem, tako da lahko postopek izvede naslednji del kode. Neblokirajoče V / I operacije omogočajo funkcijo povratnega klica, ki se pokliče po zaključku operacije.

Povratni klici

Povratni klic je funkcija podana kot argument v drugo funkcijo, ki se potem lahko sklicuje (imenovano nazaj) znotraj zunanjega delovanja za dokončanje neke vrste ukrepov v primernem času. Klic je lahko takojšen (sinhroniziraj povratni klic) ali pa se zgodi pozneje (asinhroniziran povratni klic).

// Sync callback function greetings(callback) { callback(); } greetings(() => { console.log('Hi'); }); moreWork(); // will run after console.log // Async callback const fs = require('fs'); fs.readFile('/file.md', function callback(err, data) { // fs.readFile is an async method provided by Node if (err) throw err; console.log(data); }); moreWork(); // will run before console.log 

V prvem primeru se funkcija povratnega klica pokliče takoj znotraj funkcije zunanjih pozdravov in se pred moreWork()nadaljevanjem prijavi v konzolo .

V drugem primeru fs.readFile (asinhronska metoda, ki jo ponuja Node) prebere datoteko in po zaključku pokliče funkcijo povratnega klica z napako ali vsebino datoteke. V tem času lahko program nadaljuje izvajanje kode.

Asinhni povratni klic se lahko pokliče, ko se zgodi dogodek ali ko se opravilo dokonča. Blokiranje preprečuje tako, da medtem omogoča izvajanje druge kode.

Namesto postopkovnega branja kode od zgoraj navzdol lahko asinhroni programi izvajajo različne funkcije v različnih časih glede na vrstni red in hitrost, ki se zgodi v prejšnjih funkcijah, kot so zahteve http ali branje datotečnega sistema. Uporabljajo se, ko ne veste, kdaj se bo končala katera asinhrona operacija.

Izogibajte se " peklu povratnega klica ", v katerem so povratni klici ugnezdeni v druge povratne klice globoko na več ravneh, zaradi česar je koda težko razumljiva, vzdrževana in odpravljena napak.

Dogodki in programiranje na podlagi dogodkov

Dogodki so dejanja, ki jih ustvari uporabnik ali sistem, na primer klik, dokončan prenos datoteke ali napaka strojne ali programske opreme.

Dogodkovno programiranje je programska paradigma, pri kateri tok programa določajo dogodki. Program, ki temelji na dogodkih, izvaja dejanja kot odziv na dogodke. Ko se dogodek zgodi, sproži funkcijo povratnega klica.

Zdaj pa poskusimo razumeti Node in preveriti, kako so vsi ti povezani z njim.

Node.js: kaj je to, zakaj je bilo ustvarjeno in kako deluje?

Preprosto povedano, Node.js je platforma, ki izvaja strežniške programe JavaScript, ki lahko komunicirajo z I / O viri, kot so omrežja in datotečni sistemi.

Ko je Ryan Dahl leta 2009 ustvaril Node, je trdil, da se z I / O ravna nepravilno, zaradi sinhronega programiranja pa je blokiral celoten postopek.

Tradicionalne tehnike spletnega strežnika uporabljajo model niti, kar pomeni eno nit za vsako zahtevo. Ker v operaciji V / I zahteva večino časa čaka, da se dokonča, intenzivni V / I scenariji vključujejo veliko količino neuporabljenih virov (kot je pomnilnik), povezanih s temi nitmi. Zato se model "ena nit na zahtevo" za strežnik ne prilagaja dobro.

Dahl je trdil, da bi morala biti programska oprema sposobna večopravilnosti, in predlagal odpravo časa, porabljenega za čakanje na vrnitev rezultatov V / I. Namesto modela niti je dejal, da je pravi način za obvladovanje več sočasnih povezav ta, da imamo en nit, zanko dogodkov in neblokirajoče vhode. Na primer, ko pošljete poizvedbo v bazo podatkov, namesto da bi čakali na odgovor, ji pokličete povratni klic, da se lahko vaše izvajanje izvaja skozi ta stavek in nadaljuje z drugimi stvarmi. Ko se rezultati vrnejo, lahko izvedete povratni klic.

The event loop is what allows Node.js to perform non-blocking I/O operations despite the fact that JavaScript is single-threaded. The loop, which runs on the same thread as the JavaScript code, grabs a task from the code and executes it. If the task is async or an I/O operation the loop offloads it to the system kernel, like in the case for new connections to the server, or to a thread pool, like file system related operations. The loop then grabs the next task and executes it.

Since most modern kernels are multi-threaded, they can handle multiple operations executing in the background. When one of these operations completes (this is an event), the kernel tells Node.js so that the appropriate callback (the one that depended on the operation completing) may be added to the poll queue to eventually be executed.

Node keeps track of unfinished async operations, and the event loop keeps looping to check if they are finished until all of them are.

To accommodate the single-threaded event loop, Node.js uses the libuv library, which, in turn, uses a fixed-sized thread pool that handles the execution of some of the non-blocking asynchronous I/O operations in parallel. The main thread call functions post tasks to the shared task queue, which threads in the thread pool pull and execute.

Inherently non-blocking system functions such as networking translate to kernel-side non-blocking sockets, while inherently blocking system functions such as file I/O run in a blocking way on their own threads. When a thread in the thread pool completes a task, it informs the main thread of this, which in turn, wakes up and executes the registered callback.

The above image is taken from Philip Roberts’ presentation at JSConf EU: What the heck is the event loop anyway? I recommend watching the full video to get a high level idea about how the event loop works.

The diagram explains how the event loop works with the browser but it looks basically identical for Node. Instead of web APIs we would have Node APIs.

According to the presentation, the call stack (aka execution stack or “the stack”) is a data structure which records where in the program we are. If we step into a function, we put something onto the stack. If we return from a function, we pop it off the top of the stack.

This is how the code in the diagram is processed when we run it:

  1. Push main() onto the stack (the file itself)
  2. Push console.log(‘Hi’); onto the stack, which executes immediately logging “Hi” to the console and gets popped off the stack
  3. Push setTimeout(cb, 5000) onto the stack. setTimeout is an API provided by the browser (on the backend it would be a Node API). When setTimeout is called with the callback function and delay arguments, the browser kicks off a timer with the delay time
  4. The setTimeout call is completed and gets popped off the stack
  5. Push console.log(‘JSConfEU’); onto the stack, which executes immediately logging “JSConfEU” to the console and gets popped off the stack
  6. main() gets popped off the stack
  7. After 5000 milliseconds the API timer completes and the callback gets moved to the task queue
  8. The event loop checks if the stack is empty because JavaScript, being single-threaded, can only do one thing at a time (setTimeout is not a guaranteed but a minimum time to execution). If the stack is empty it takes the first thing on the queue and pushes it onto the stack. Therefore the loop pushes the callback onto the stack
  9. The callback gets executed, logs “there” to the console and gets popped off the stack. And we are done

If you want to go even deeper into the details on how Node.js, libuv, the event loop and the thread pool work, I suggest checking the resources on the reference section at the end, in particular this, this and this along with the Node docs.

Node.js: why and where to use it?

Since almost no function in Node directly performs I/O, the process never blocks (I/O operations are offloaded and executed asynchronously in the system), making it a good choice to develop highly scalable systems.

Due to its event-driven, single-threaded event loop and asynchronous non-blocking I/O model, Node.js performs best on intense I/O applications requiring speed and scalability with lots of concurrent connections, like video & audio streaming, real-time apps, live chats, gaming apps, collaboration tools, or stock exchange software.

Node.js may not be the right choice for CPU intensive operations. Instead the traditional thread model may perform better.

npm

npm is the default package manager for Node.js and it gets installed into the system when Node.js is installed. It can manage packages that are local dependencies of a particular project, as well as globally-installed JavaScript tools.

www.npmjs.com hosts thousands of free libraries to download and use in your program to make development faster and more efficient. However, since anybody can create libraries and there’s no vetting process for submission, you have to be careful about low quality, insecure, or malicious ones. npm relies on user reports to take down packages if they violate policies, and to help you decide, it includes statistics like number of downloads and number of depending packages.

How to run code in Node.js

Start by installing Node on your computer if you don’t have it already. The easiest way is to visit nodejs.org and click to download it. Unless you want or need to have access to the latest features, download the LTS (Long Term Support) version for you operating system.

You run a Node application from your computer’s terminal. For example make a file “app.js” and add console.log(‘Hi’); to it. On your terminal change the directory to the folder where this file belongs to and run node app.js. It will log “Hi” to the console. ?

References

Here are some of the interesting resources I reviewed during the writing of the article.

Node.js presentations by its author:

  • Original Node.js presentation by Ryan Dahl at JSConf 2009
  • 10 Things I Regret About Node.js by Ryan Dahl at JSConf EU 2018

Vozlišče, zanka dogodka in predstavitve knjižnice libuv:

  • Kaj za vraga je sploh zanka dogodkov? Philip Roberts, JSConf EU
  • Node.js Pojasnil Jeff Kunkle
  • V The Loop Jakea Archibalda na JSConf Asia 2018
  • Vse, kar morate vedeti o zanki dogodkov Node.js, avtor Bert Belder
  • Globok potop v libuv Saula Ibarre Coretgeja na NodeConf EU 2016

Dokumenti vozlišča:

  • O Node.js
  • Loop dogodkov Node.js, časovniki in process.nextTick ()
  • Pregled blokiranja in neblokiranja

Dodatni viri:

  • Umetnost vozlišča Maksa Ogdena
  • Max Ogden, pekel za povratni klic
  • Kaj je blokirni ali asinhroni V / I v Node.js? na Stack Overflow
  • Dogodkovno programiranje na Wikipediji
  • Node.js na Wikipediji
  • Nit na Wikipediji
  • libuv

Hvala za branje.