Reaktiivinen vs ennakoiva ohjelmointi RxJS: llä

(Ben Copeland) (25. maaliskuuta , 2020)

Jos käytämme ReactiveX-kirjastoja, kuten RxJS, tiedämme, että toteutamme teknisesti reaktiivisen ohjelmointiratkaisun – käytämme RxJS: n havainnoitavien virtojen käsitettä ja koodimme reagoi uusiin tapahtumiin streamissa. Vaikka käytämme reaktiivista kirjastoa, on kuitenkin mahdollista suunnitella ohjelmistomme pikemminkin proaktiivisen kuin reaktiivisen paradigman mukaan. Nämä kirjastot palvelevat meitä kuitenkin paljon paremmin, jos meillä on reaktiivinen mielentila.

Otetaan hetki eroon proaktiivisen ja reaktiivisen lähestymistavan välillä. Ennakoiva tarkoitan lähestymistapaa, jossa julkaisija (tai aihe) on tietoinen ulkoisista tilaajista ja heidän toivomastaan ​​tilasta ja ajaa päivityksiä tunnetuille tilaajille. Yhdistyminen kuluttajaan tapahtuu julkaisijan puolella. Tarkistamalla reaktiivinen tarkoitan lähestymistapaa, jossa tilaaja saa päivityksen ja käsittelee sen itse.

Harkitse viimeaikaista skenaariota, jonka tapasimme IQueue for Clinics -sivustolla. Meillä on Angular-sovellus, joka käyttää paljon RxJS: ää, ja halusimme julkaista kaikki muutokset, jotka tulevat esiin komponentin ngOnChanges elinkaarikoukussa virtaan, jonka tilaajat voivat kuluttaa.

Tässä on yksinkertaistettu esimerkki, jossa meillä on komponentti, jolla on kolme ominaisuutta @Input, ja meillä on joitain komponentteja, jotka haluavat seurata objektia, jolla on viimeisin arvo jokaisesta kolme ominaisuutta.

interface IChanges { a: string; b: string; c: string; }

@Component({...})
class SomeComponent {
@Input() a;
@Input() b;
@Input() c;

changes$: BehaviorSubject = new BehaviorSubject({
a: null,
b: null,
c: null,
});

allLatestChanges: IChanges;

ngOnChanges(changes: SimpleChanges) {
if (changes.a) {
changes$.next(Object.assign(allLatestChanges, { a: changes.a.currentValue });
}
if (changes.b) {
changes$.next(Object.assign(allLatestChanges, { b: changes.b.currentValue });
}
if (changes.c) {
changes$.next(Object.assign(allLatestChanges, { c: changes.c.currentValue });
}
}
}

Yllä olevassa esimerkissä luomme objektin, jolla on kaikki kolme välimuistiin tallentamisen mahdollista ominaisuutta, ja sitten ngOnChanges, yhdistämme päivitykset mistä tahansa kolmesta ominaisuudesta kyseiseen objektiin ja päivitämme virran yhdistetyn objektin uusimmalla arvolla. Tämä on ennakoiva lähestymistapa – kohde tietää tarkkailijan tarkoituksen. Jos lisätään toinen tarkkailija, jolla on erilainen tarkoitus, tai jos nykyisen tarkkailijan tarkoitus muuttuu, aiheen toteutusta on muutettava.

refraktoida sitä reaktiivisemmaksi. Jotta tämä tapahtuisi, tarvitsimme kohteen yksinkertaisesti julkaista viimeisin arvo ilman kuluttajien tietämystä. Seuraava on päivitetty versio aikaisemmasta komponentista, joka julkaisee vain nykyiset päivitykset.

@Component({...})
class SomeComponent {
@Input() a;
@Input() b;
@Input() c;

changes$: Subject = new Subject();

ngOnChanges(changes: SimpleChanges) {
changes$.next(changes);
}
}

Ja sitten käsittelemme yhdistämisen yhdeksi objektiksi luokassa, joka kuluttaa havainnoitava.

defaultAcc = {
a: null,
b: null,
c: null,
}

allChanges$ = changes$
.pipe(
scan((acc, curr) =>
Object.assign(acc, Object.fromEntries(
Object.entries(curr).map((k,v) => { [k]: v.currentValue })
)
)), defaultAcc
);

Nyt havaittava julkaisee vain uusia tapahtumia, ja tilaaja valjastaa yhden RxJS: n monista tehokkaista putkista, joita kutsutaan skannaukseksi kertyneen tilan ylläpitämiseksi. kaikkien virran läpi tulevien mahdollisten ominaisuuksien uusimmista arvoista. Ajattele skannausta pienennystoimintona, joka tuottaa viimeisimmän kertyneen arvon jokaisesta tapahtumasta. Refaktoroinnin jälkeen meillä on siis yleiskäyttöinen aihe, joka lähettää kaikki mahdolliset ngOnChanges -tapahtumat. Jos lisäät @Inputs, lisää ne luonnollisesti myös. Kaikki tilaajat, jotka kuuntelevat virtaa, voivat tehdä mitä näkevät sopivaksi tapahtumavirtaan – kerätäänkö kohteena kaikkien tapahtumien kohde vai kuunnellaanko vain muutoksia yhdellä omaisuudella.

Vaikka RxJS antaa Meillä on työkaluja reaktiivisen ohjelmoinnin suorittamiseen, mutta silti on mahdollista suunnitella koodi, joka noudattaa ennakoivampaa paradigmaa. Reagoivuuden lisäämiseksi voimme pitää julkaisumme mahdollisimman agnostisena ja sallia tai tilausten toteuttaa monimutkaisemman logiikan.

Vastaa

Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *