Naršykite reaktyvųjį programavimą JavaScript naudojant RxJS. Išmokite stebimųjų srautų, šablonų ir praktinių pritaikymų, kad sukurtumėte jautrias ir plečiamas programas.
JavaScript Reaktyvusis Programavimas: RxJS Šablonai ir Stebimieji Srautai
Nuolat besikeičiančiame moderniosios žiniatinklio kūrimo pasaulyje, svarbiausia yra kurti jautrias, plečiamas ir lengvai prižiūrimas programas. Reaktyvusis Programavimas (RP) suteikia galingą paradigmą asinchroninių duomenų srautų valdymui ir pokyčių sklaidai visoje programoje. Tarp populiarių bibliotekų, skirtų RP įgyvendinti JavaScript kalboje, RxJS (Reactive Extensions for JavaScript) išsiskiria kaip tvirtas ir universalus įrankis.
Kas yra Reaktyvusis Programavimas?
Iš esmės, Reaktyvusis Programavimas yra susijęs su asinchroninių duomenų srautų valdymu ir pokyčių sklaida. Įsivaizduokite skaičiuoklę, kurioje atnaujinus vieną langelį, automatiškai perskaičiuojamos susijusios formulės. Tai ir yra RP esmė – deklaratyviai ir efektyviai reaguoti į duomenų pasikeitimus.
Tradicinis imperatyvusis programavimas dažnai apima būsenos valdymą ir rankinį komponentų atnaujinimą reaguojant į įvykius. Dėl to kodas gali tapti sudėtingas ir linkęs į klaidas, ypač dirbant su asinchroninėmis operacijomis, tokiomis kaip tinklo užklausos ar vartotojo sąveikos. RP tai supaprastina, viską traktuodamas kaip duomenų srautą ir suteikdamas operatorius šiems srautams transformuoti, filtruoti ir sujungti.
Pristatome RxJS: Reaktyvieji Plėtiniai JavaScript kalbai
RxJS yra biblioteka, skirta kurti asinchronines ir įvykiais pagrįstas programas naudojant stebimąsias sekas. Ji suteikia galingų operatorių rinkinį, leidžiantį lengvai manipuliuoti duomenų srautais. RxJS remiasi Stebėtojo (Observer), Iteratoriaus (Iterator) šablonais ir Funkcinio Programavimo koncepcijomis, kad efektyviai valdytų įvykių ar duomenų sekas.
Pagrindinės RxJS sąvokos:
- Stebimieji (Observables): Atstovauja duomenų srautą, kurį gali stebėti vienas ar daugiau Stebėtojų (Observers). Jie yra „tingūs“ (lazy) ir pradeda skleisti vertes tik tada, kai yra prenumeruojami.
- Stebėtojai (Observers): Vartoja duomenis, kuriuos skleidžia Stebimieji. Jie turi tris metodus:
next()
vertėms gauti,error()
klaidoms apdoroti ircomplete()
srauto pabaigai signalizuoti. - Operatoriai (Operators): Funkcijos, kurios transformuoja, filtruoja, sujungia ar manipuliuoja Stebimaisiais. RxJS suteikia platų operatorių spektrą įvairiems tikslams.
- Subjektai (Subjects): Veikia ir kaip Stebimieji, ir kaip Stebėtojai, leisdami transliuoti duomenis keliems prenumeratoriams ir taip pat įstumti duomenis į srautą.
- Planuokliai (Schedulers): Kontroliuoja Stebimųjų lygiagretumą, leisdami vykdyti kodą sinchroniškai ar asinchroniškai, skirtingose gijose ar su konkrečiais vėlavimais.
Stebimieji Srautai Išsamiau
Stebimieji yra RxJS pagrindas. Jie atstovauja duomenų srautą, kurį galima stebėti laikui bėgant. Stebimasis skleidžia vertes savo prenumeratoriams, kurie vėliau gali apdoroti tas vertes arba į jas reaguoti. Įsivaizduokite tai kaip vamzdyną, kuriuo duomenys teka iš šaltinio vienam ar daugiau vartotojų.
Stebimųjų Kūrimas:
RxJS suteikia kelis būdus kurti Stebimuosius:
Observable.create()
: Žemo lygio metodas, suteikiantis visišką Stebimojo elgsenos kontrolę.from()
: Konvertuoja masyvą, pažadą (promise), iteruojamąjį objektą arba į Stebimąjį panašų objektą į Stebimąjį.of()
: Sukuria Stebimąjį, kuris skleidžia verčių seką.interval()
: Sukuria Stebimąjį, kuris skleidžia skaičių seką nurodytu intervalu.timer()
: Sukuria Stebimąjį, kuris skleidžia vieną vertę po nurodyto delsimo arba skleidžia skaičių seką fiksuotu intervalu po delsimo.fromEvent()
: Sukuria Stebimąjį, kuris skleidžia įvykius iš DOM elemento ar kito įvykių šaltinio.
Pavyzdys: Stebimojo Kūrimas iš Masyvo
```javascript import { from } from 'rxjs'; const myArray = [1, 2, 3, 4, 5]; const myObservable = from(myArray); myObservable.subscribe( value => console.log('Gauta:', value), error => console.error('Klaida:', error), () => console.log('Užbaigta') ); // Išvestis: // Gauta: 1 // Gauta: 2 // Gauta: 3 // Gauta: 4 // Gauta: 5 // Užbaigta ```
Pavyzdys: Stebimojo Kūrimas iš Įvykio
```javascript import { fromEvent } from 'rxjs'; const button = document.getElementById('myButton'); const clickObservable = fromEvent(button, 'click'); clickObservable.subscribe( event => console.log('Mygtukas paspaustas!', event) ); ```
Stebimųjų Prenumerata:
Norėdami pradėti gauti vertes iš Stebimojo, turite jį prenumeruoti naudodami subscribe()
metodą. subscribe()
metodas priima iki trijų argumentų:
next
: Funkcija, kuri bus iškviesta kiekvienai Stebimojo išskleistai vertei.error
: Funkcija, kuri bus iškviesta, jei Stebimasis išskleis klaidą.complete
: Funkcija, kuri bus iškviesta, kai Stebimasis baigs darbą (signalizuos srauto pabaigą).
subscribe()
metodas grąžina Prenumeratos (Subscription) objektą, kuris atstovauja ryšį tarp Stebimojo ir Stebėtojo. Galite naudoti Prenumeratos objektą norėdami atšaukti prenumeratą nuo Stebimojo, taip sustabdydami tolesnį verčių skleidimą.
Prenumeratos Atšaukimas:
Prenumeratos atšaukimas yra labai svarbus siekiant išvengti atminties nutekėjimo, ypač dirbant su ilgai gyvuojančiais Stebimaisiais arba Stebimaisiais, kurie dažnai skleidžia vertes. Galite atšaukti Stebimojo prenumeratą iškviesdami unsubscribe()
metodą ant Prenumeratos objekto.
```javascript import { interval } from 'rxjs'; const myInterval = interval(1000); const subscription = myInterval.subscribe( value => console.log('Intervalas:', value) ); // Po 5 sekundžių atšaukti prenumeratą setTimeout(() => { subscription.unsubscribe(); console.log('Prenumerata atšaukta!'); }, 5000); // Išvestis (apytiksliai): // Intervalas: 0 // Intervalas: 1 // Intervalas: 2 // Intervalas: 3 // Intervalas: 4 // Prenumerata atšaukta! ```
RxJS Operatoriai: Duomenų Srautų Transformavimas ir Filtravimas
RxJS operatoriai yra bibliotekos širdis. Jie leidžia transformuoti, filtruoti, sujungti ir manipuliuoti Stebimaisiais deklaratyviu ir komponuojamu būdu. Yra daugybė prieinamų operatorių, kurių kiekvienas atlieka tam tikrą funkciją. Štai keletas dažniausiai naudojamų operatorių:
Transformavimo Operatoriai:
map()
: Pritaikoma funkcija kiekvienai Stebimojo išskleistai vertei ir skleidžiamas rezultatas. Panašus į masyvųmap()
metodą.pluck()
: Ištraukia konkrečią savybę iš kiekvienos Stebimojo išskleistos vertės.scan()
: Pritaikoma kaupimo funkcija šaltinio Stebimajam ir grąžinamas kiekvienas tarpinis rezultatas.buffer()
: Surenka vertes iš šaltinio Stebimojo į masyvą ir skleidžia masyvą, kai įvykdoma tam tikra sąlyga.window()
: Panašus įbuffer()
, bet vietoj masyvo skleidimo, jis skleidžia Stebimąjį, kuris atstovauja verčių langą.
Pavyzdys: map()
operatoriaus naudojimas
```javascript import { from } from 'rxjs'; import { map } from 'rxjs/operators'; const numbers = from([1, 2, 3, 4, 5]); const squaredNumbers = numbers.pipe( map(x => x * x) ); squaredNumbers.subscribe(value => console.log('Kvadratu:', value)); // Išvestis: // Kvadratu: 1 // Kvadratu: 4 // Kvadratu: 9 // Kvadratu: 16 // Kvadratu: 25 ```
Filtravimo Operatoriai:
filter()
: Skleidžia tik tas vertes, kurios atitinka tam tikrą sąlygą.debounceTime()
: Atideda verčių skleidimą, kol praeis tam tikras laiko tarpas be naujų verčių. Naudinga tvarkant vartotojo įvestį ir išvengiant perteklinio užklausų skaičiaus.distinctUntilChanged()
: Skleidžia tik tas vertes, kurios skiriasi nuo ankstesnės vertės.take()
: Skleidžia tik pirmąsias N verčių iš Stebimojo.skip()
: Praleidžia pirmąsias N verčių iš Stebimojo ir skleidžia likusias vertes.
Pavyzdys: filter()
operatoriaus naudojimas
```javascript import { from } from 'rxjs'; import { filter } from 'rxjs/operators'; const numbers = from([1, 2, 3, 4, 5, 6]); const evenNumbers = numbers.pipe( filter(x => x % 2 === 0) ); evenNumbers.subscribe(value => console.log('Lyginis:', value)); // Išvestis: // Lyginis: 2 // Lyginis: 4 // Lyginis: 6 ```
Kombinavimo Operatoriai:
merge()
: Sujungia kelis Stebimuosius į vieną Stebimąjį.concat()
: Sujungia kelis Stebimuosius, skleisdamas vertes iš kiekvieno Stebimojo nuosekliai.combineLatest()
: Sujungia naujausias vertes iš kelių Stebimųjų ir skleidžia naują vertę, kai bet kuris iš šaltinio Stebimųjų skleidžia vertę.zip()
: Sujungia vertes iš kelių Stebimųjų pagal jų indeksą ir skleidžia naują vertę kiekvienai kombinacijai.withLatestFrom()
: Sujungia naujausią vertę iš kito Stebimojo su dabartine verte iš šaltinio Stebimojo.
Pavyzdys: combineLatest()
operatoriaus naudojimas
```javascript import { interval, combineLatest } from 'rxjs'; import { map } from 'rxjs/operators'; const interval1 = interval(1000); const interval2 = interval(2000); const combinedIntervals = combineLatest( interval1, interval2, (x, y) => `Intervalas 1: ${x}, Intervalas 2: ${y}` ); combinedIntervals.subscribe(value => console.log(value)); // Išvestis (apytiksliai): // Intervalas 1: 0, Intervalas 2: 0 // Intervalas 1: 1, Intervalas 2: 0 // Intervalas 1: 1, Intervalas 2: 1 // Intervalas 1: 2, Intervalas 2: 1 // Intervalas 1: 2, Intervalas 2: 2 // ... ```
Dažniausi RxJS Šablonai
RxJS suteikia kelis galingus šablonus, kurie gali supaprastinti įprastas asinchroninio programavimo užduotis:
Debouncing (atmetimas dėl dažnumo):
Operatorius debounceTime()
naudojamas atidėti verčių skleidimą, kol praeis tam tikras laiko tarpas be naujų verčių. Tai ypač naudinga tvarkant vartotojo įvestį, pavyzdžiui, paieškos užklausas ar formų pateikimą, kai norite išvengti perteklinio užklausų siuntimo į serverį.
Pavyzdys: Paieškos įvesties „Debouncing“
```javascript import { fromEvent } from 'rxjs'; import { map, debounceTime, distinctUntilChanged } from 'rxjs/operators'; const searchInput = document.getElementById('searchInput'); const searchObservable = fromEvent(searchInput, 'keyup').pipe( map((event: any) => event.target.value), debounceTime(300), // Palaukite 300ms po kiekvieno klavišo paspaudimo distinctUntilChanged() // Skleisti tik jei vertė pasikeitė ); searchObservable.subscribe(searchTerm => { console.log('Ieškoma:', searchTerm); // Atlikite API užklausą paieškos terminui }); ```
Throttling (dažnio ribojimas):
Operatorius throttleTime()
riboja greitį, kuriuo skleidžiamos vertės iš Stebimojo. Jis skleidžia pirmąją vertę, gautą per nurodytą laiko langą, ir ignoruoja vėlesnes vertes, kol langas užsidaro. Tai naudinga ribojant įvykių dažnumą, pavyzdžiui, slinkimo (scroll) ar dydžio keitimo (resize) įvykius.
Perjungimas (Switching):
Operatorius switchMap()
naudojamas pereiti prie naujo Stebimojo, kai iš šaltinio Stebimojo išskleidžiama nauja vertė. Tai naudinga atšaukiant laukiančias užklausas, kai pradedama nauja užklausa. Pavyzdžiui, galite naudoti switchMap()
, kad atšauktumėte ankstesnę paieškos užklausą, kai vartotojas įveda naują simbolį paieškos laukelyje.
Pavyzdys: switchMap()
naudojimas „Typeahead“ paieškai
```javascript import { fromEvent, of } from 'rxjs'; import { map, debounceTime, distinctUntilChanged, switchMap, catchError } from 'rxjs/operators'; const searchInput = document.getElementById('searchInput'); const searchObservable = fromEvent(searchInput, 'keyup').pipe( map((event: any) => event.target.value), debounceTime(300), distinctUntilChanged(), switchMap(searchTerm => { // Atlikite API užklausą paieškos terminui return searchAPI(searchTerm).pipe( catchError(error => { console.error('Klaida ieškant:', error); return of([]); // Klaidos atveju grąžinkite tuščią masyvą }) ); }) ); searchObservable.subscribe(results => { console.log('Paieškos rezultatai:', results); // Atnaujinkite vartotojo sąsają su paieškos rezultatais }); function searchAPI(searchTerm: string) { // Imituokite API užklausą return of([`Rezultatas terminui ${searchTerm} 1`, `Rezultatas terminui ${searchTerm} 2`]); } ```
Praktinis RxJS Pritaikymas
RxJS yra universali biblioteka, kurią galima naudoti įvairiose srityse. Štai keletas dažniausių panaudojimo atvejų:
- Vartotojo Įvesties Apdorojimas: RxJS gali būti naudojamas vartotojo įvesties įvykiams, tokiems kaip klavišų paspaudimai, pelės paspaudimai ir formų pateikimas, apdoroti. Operatoriai, tokie kaip
debounceTime()
irthrottleTime()
, gali būti naudojami našumui optimizuoti ir pertekliniam užklausų skaičiui išvengti. - Asinchroninių Operacijų Valdymas: RxJS suteikia galingą būdą valdyti asinchronines operacijas, tokias kaip tinklo užklausos ir laikmačiai. Operatoriai, tokie kaip
switchMap()
irmergeMap()
, gali būti naudojami lygiagrečioms užklausoms tvarkyti ir laukiančioms užklausoms atšaukti. - Realaus Laiko Programų Kūrimas: RxJS puikiai tinka kurti realaus laiko programas, tokias kaip pokalbių programos ir informacinės panelės. Stebimieji gali būti naudojami duomenų srautams iš „WebSockets“ ar „Server-Sent Events“ (SSE) atvaizduoti.
- Būsenos Valdymas: RxJS gali būti naudojamas kaip būsenos valdymo sprendimas tokiose karkasuose kaip Angular, React ir Vue.js. Stebimieji gali būti naudojami programos būsenai atvaizduoti, o operatoriai – būsenai transformuoti ir atnaujinti, reaguojant į vartotojo veiksmus ar įvykius.
RxJS su Populiariais Karkasais
Angular:
Angular stipriai remiasi RxJS asinchroninių operacijų tvarkymui ir duomenų srautų valdymui. HttpClient
paslauga Angular karkase grąžina Stebimuosius, o RxJS operatoriai plačiai naudojami duomenims, gautiems iš API užklausų, transformuoti ir filtruoti. Angular pokyčių aptikimo mechanizmas taip pat naudoja RxJS, kad efektyviai atnaujintų vartotojo sąsają, reaguojant į duomenų pasikeitimus.
Pavyzdys: RxJS naudojimas su Angular HttpClient
```typescript
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class DataService {
private apiUrl = 'https://api.example.com/data';
constructor(private http: HttpClient) { }
getData(): Observable
React:
Nors React neturi integruoto RxJS palaikymo, jį galima lengvai integruoti naudojant bibliotekas, tokias kaip rxjs-hooks
ar use-rx
. Šios bibliotekos suteikia pasirinktinius „kablius“ (hooks), kurie leidžia prenumeruoti Stebimuosius ir valdyti prenumeratas React komponentuose. RxJS gali būti naudojamas React karkase asinchroniniam duomenų gavimui, komponentų būsenos valdymui ir reaktyvių vartotojo sąsajų kūrimui.
Pavyzdys: RxJS naudojimas su React „kabliais“
```javascript import React, { useState, useEffect } from 'react'; import { Subject } from 'rxjs'; import { scan } from 'rxjs/operators'; function Counter() { const [count, setCount] = useState(0); const increment$ = new Subject(); useEffect(() => { const subscription = increment$.pipe( scan(acc => acc + 1, 0) ).subscribe(setCount); return () => subscription.unsubscribe(); }, []); return (
Skaičius: {count}
Vue.js:
Vue.js taip pat neturi integruoto RxJS palaikymo, tačiau jį galima naudoti su bibliotekomis, tokiomis kaip vue-rx
, arba rankiniu būdu valdant prenumeratas Vue komponentuose. RxJS gali būti naudojamas Vue.js karkase panašiais tikslais kaip ir React, pavyzdžiui, asinchroniniam duomenų gavimui ir komponentų būsenos valdymui.
Gerosios RxJS Naudojimo Praktikos
- Atšaukite Stebimųjų Prenumeratas: Visada atšaukite Stebimųjų prenumeratas, kai jų nebereikia, kad išvengtumėte atminties nutekėjimo. Naudokite Prenumeratos objektą, grąžinamą
subscribe()
metodo, prenumeratai atšaukti. - Naudokite
pipe()
Metodą: Naudokitepipe()
metodą operatoriams sujungti į skaitomą ir lengvai prižiūrimą grandinę. - Korektiškai Apdorokite Klaidas: Naudokite
catchError()
operatorių klaidoms apdoroti ir neleisti joms plisti aukštyn Stebimojo grandine. - Pasirinkite Tinkamus Operatorius: Pasirinkite tinkamus operatorius jūsų konkrečiam atvejui. RxJS suteikia platų operatorių spektrą, todėl svarbu suprasti jų paskirtį ir elgseną.
- Išlaikykite Stebimuosius Paprastus: Venkite kurti pernelyg sudėtingus Stebimuosius. Sudėtingas operacijas suskaidykite į mažesnius, lengviau valdomus Stebimuosius.
Pažangios RxJS Koncepcijos
Subjektai (Subjects):
Subjektai veikia ir kaip Stebimieji, ir kaip Stebėtojai. Jie leidžia transliuoti duomenis keliems prenumeratoriams ir taip pat įstumti duomenis į srautą. Yra keletas Subjektų tipų, įskaitant:
- Subject: Pagrindinis Subjektas, kuris transliuoja vertes visiems prenumeratoriams.
- BehaviorSubject: Reikalauja pradinės vertės ir skleidžia dabartinę vertę naujiems prenumeratoriams.
- ReplaySubject: Kaupia nurodytą skaičių verčių ir atkuria jas naujiems prenumeratoriams.
- AsyncSubject: Skleidžia tik paskutinę vertę, kai Stebimasis baigia darbą.
Planuokliai (Schedulers):
Planuokliai kontroliuoja Stebimųjų lygiagretumą. Jie leidžia vykdyti kodą sinchroniškai ar asinchroniškai, skirtingose gijose ar su konkrečiais vėlavimais. RxJS suteikia kelis integruotus planuoklius, įskaitant:
queueScheduler
: Planuoja užduotis vykdyti dabartinėje JavaScript gijoje, po dabartinio vykdymo konteksto.asapScheduler
: Planuoja užduotis vykdyti dabartinėje JavaScript gijoje, kuo greičiau po dabartinio vykdymo konteksto.asyncScheduler
: Planuoja užduotis vykdyti asinchroniškai, naudojantsetTimeout
arbasetInterval
.animationFrameScheduler
: Planuoja užduotis vykdyti kitame animacijos kadre.
Išvada
RxJS yra galinga biblioteka, skirta kurti reaktyvias programas JavaScript kalboje. Įvaldę Stebimuosius, operatorius ir įprastus šablonus, galite kurti jautresnes, plečiamas ir lengviau prižiūrimas programas. Nesvarbu, ar dirbate su Angular, React, Vue.js, ar su grynu JavaScript, RxJS gali ženkliai pagerinti jūsų gebėjimą tvarkyti asinchroninius duomenų srautus ir kurti sudėtingas vartotojo sąsajas.
Pasinaudokite reaktyviojo programavimo galia su RxJS ir atverkite naujas galimybes savo JavaScript programoms!