Tere kõigile. Võtke ühendust Sergei Omelnitskiga. Mitte nii kaua aega tagasi võõrustasin reaktiivse programmeerimise voogu, kus rääkisin JavaScripti asünkroonist. Täna tahaksin selle materjali kokku võtta.
Kuid enne põhimaterjaliga alustamist peame tegema sissejuhatuse. Alustame definitsioonidega: mis on virn ja järjekord?
Virna on kogumik, mille elemendid otsitakse LIFO põhimõttel "viimane sisse, esimene välja".
Pöörake on kogum, mille elemendid saadakse põhimõttel (“first in, first out” FIFO
Olgu, jätkame.
JavaScript on ühe lõimega programmeerimiskeel. See tähendab, et sellel on ainult üks täitmise lõim ja üks virn, kus funktsioonid on täitmiseks järjekorras. Seetõttu saab JavaScript teha ainult ühe toimingu korraga, samas kui teised toimingud ootavad pinus oma järjekorda, kuni neid kutsutakse.
Kõnede virn on andmestruktuur, mis lihtsamalt öeldes salvestab informatsiooni selle koha kohta programmis, kus me asume. Kui hüppame funktsiooni juurde, lükkame selle sisestuse virna ülaossa. Funktsioonist naastes tõstame virnast ülemise elemendi ja jõuame sinna, kust selle funktsiooni kutsusime. See on kõik, mida virn teha saab. Ja nüüd väga huvitav küsimus. Kuidas siis asünkroonsus JavaScriptis töötab?
Tegelikult on brauserites lisaks virnale ka spetsiaalne järjekord nn WebAPI-ga töötamiseks. Funktsioonid sellest järjekorrast käivitatakse järjekorras alles pärast pinu täielikku tühjendamist. Alles pärast seda paigutatakse need täitmiseks järjekorrast virna. Kui virna peal on parasjagu vähemalt üks element, siis need virna peale ei pääse. Just seetõttu on funktsioonide ajalõpu järgi kutsumine sageli ajaliselt ebatäpne, kuna funktsioon ei pääse järjekorrast virna, kui see on täis.
Vaatame järgmist näidet ja vaatame seda samm-sammult läbi. Vaatame ka, mis süsteemis toimub.
Vaatasime, kuidas asünkroonsust JavaScriptis rakendatakse. Räägime nüüd lühidalt asünkroonse koodi arengust.
Asünkroonse koodi areng.
a(function (resultsFromA) {
b(resultsFromA, function (resultsFromB) {
c(resultsFromB, function (resultsFromC) {
d(resultsFromC, function (resultsFromD) {
e(resultsFromD, function (resultsFromE) {
f(resultsFromE, function (resultsFromF) {
console.log(resultsFromF);
})
})
})
})
})
});
Asünkroonset programmeerimist, nagu me seda JavaScriptis teame, saab teha ainult funktsioonidega. Neid saab edastada nagu kõiki teisi muutujaid teistele funktsioonidele. Nii sündisid tagasihelistamised. Ja see on lahe, lõbus ja tuline, kuni see muutub kurbuseks, melanhooliaks ja kurbuseks. Miks? Jah, see on lihtne:
Koodi keerukuse kasvades muutub projekt kiiresti mitmeks varjatuks pesastatud plokiks - "tagasihelistamispõrguks".
Vigade käsitlemine võib kergesti tähelepanuta jääda.
Sa ei saa avaldisi tagastada koos return-iga.
Promise tulekuga on olukord veidi paremaks läinud.
new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 2000);
}).then((result) => {
alert(result);
return result + 2;
}).then((result) => {
throw new Error('FAILED HERE');
alert(result);
return result + 2;
}).then((result) => {
alert(result);
return result + 2;
}).catch((e) => {
console.log('error: ', e);
});
Ilmusid lubadusketid, mis parandasid koodi loetavust
Eraldi oli vigade pealtkuulamise meetod
Paralleelne täitmine lisatud Promise.all-iga
Pesastatud asünkrooniat saame lahendada käsuga async/await
Kuid lubadusel on omad piirangud. Näiteks lubadust, ilma tamburiiniga tantsimata, ei saa tühistada ja mis kõige tähtsam, see töötab ühe väärtusega.
Noh, siin me läheneme sujuvalt reaktiivsele programmeerimisele. Väsinud? Hea on see, et võite minna kajakaid pruulima, ajurünnakut tegema ja naasta, et rohkem lugeda. Ja ma jätkan.
Reaktiivne programmeerimine - programmeerimisparadigma, mis keskendub andmevoogudele ja muutuste levikule. Vaatame lähemalt, mis on andmevoog.
// Получаем ссылку на элемент
const input = ducument.querySelector('input');
const eventsArray = [];
// Пушим каждое событие в массив eventsArray
input.addEventListener('keyup',
event => eventsArray.push(event)
);
Kujutame ette, et meil on sisestusväli. Loome massiivi ja iga sisendsündmuse võtmeklahvi jaoks salvestame sündmuse oma massiivi. Samas märgin ära, et meie massiiv on sorteeritud aja järgi, s.t. hilisemate sündmuste indeks on suurem kui varasemate sündmuste indeks. Selline massiiv on lihtsustatud andmevoo mudel, kuid see pole veel voog. Selleks, et seda massiivi saaks ohutult vooks nimetada, peab see suutma tellijaid kuidagi teavitada uute andmete saabumisest. Nii jõuame voolu definitsioonini.
Voolu on aja järgi sorteeritud andmete massiiv, mis võib näidata, et andmed on muutunud. Kujutage nüüd ette, kui mugav on kirjutada koodi, milles peate ühe toimingu jaoks käivitama mitu sündmust koodi erinevates osades. Tellime lihtsalt voo ja see annab meile teada, kui muudatused toimuvad. Ja RxJ teek saab seda teha.
RxJS on teek asünkroonsete ja sündmustepõhiste programmidega töötamiseks, kasutades jälgitavaid järjestusi. Raamatukogu pakub peamist tüüpi Vaadeldav, mitut tüüpi abistajaid (Vaatlejad, planeerijad, subjektid) ja operaatorid nii sündmuste kui ka kogudega töötamiseks (kaardistada, filtreerida, vähendada, iga ja sarnased JavaScripti massiivist).
Saame aru selle raamatukogu põhikontseptsioonidest.
Vaadeldav, vaatleja, produtsent
Vaadeldav on esimene baastüüp, mida me vaatame. See klass sisaldab põhiosa RxJ-de rakendamisest. See on seotud jälgitava vooga, mida saab tellida tellimismeetodi abil.
Observable rakendab uuenduste loomise abimehhanismi nn vaatama. Vaatleja väärtuste allikat nimetatakse Tootja. See võib olla massiiv, iteraator, veebipesa, mingi sündmus jne. Seega võime öelda, et vaadeldav on dirigent Tootja ja Vaatleja vahel.
Observable käsitleb kolme tüüpi vaatleja sündmusi:
järgmine – uued andmed
viga - viga, kui jada katkes erandi tõttu. see sündmus tähendab ka jada lõppu.
lõpetatud – signaal jada lõpu kohta. See tähendab, et uusi andmeid enam ei tule
Vaatame demo:
Alguses töötleme väärtusi 1, 2, 3 ja 1 sekundi pärast. saame 4 ja lõpetame lõime.
Mõeldes valjusti
Ja siis sain aru, et sellest oli huvitavam rääkida kui kirjutada. 😀
Püsitellimus
Voo tellimisel loome uue klassi tellimine, mis annab meile võimaluse meetodi abil tellimusest loobuda lahkuda. Meetodi abil saame ka tellimusi rühmitada lisama. Noh, on loogiline, et saame lõimede rühmitamist kasutades kõrvaldama. Lisamise ja eemaldamise meetodid aktsepteerivad sisendiks teistsugust tellimust. Tahaksin märkida, et tellimuse tühistamisel loobume kõigist alamtellimustest, nagu nimetaksid nad ka tellimuse tühistamise meetodit. Lase käia.
Voogude tüübid
KUUM
KÜLM
Tootja luuakse väljaspool vaadeldavat
Tootja luuakse vaadeldava sees
Andmed edastatakse jälgitava loomise ajal
Andmed esitatakse tellimise ajal.
Tellimusest loobumiseks on vaja rohkem loogikat
Lõim lõpeb iseenesest
Kasutab üks-mitmele suhet
Kasutab üks-ühele suhet
Kõikidel tellimustel on sama väärtus
Tellimused on sõltumatud
Abonemendi puudumisel võivad andmed kaduda
Väljastab uuesti kõik vooväärtused uue tellimuse jaoks
Kui tuua analoogia, siis ma kujutaksin ette kuuma voogu nagu filmis kinos. Mis ajahetkel sa tulid, sellest hetkest hakkasid sa vaatama. Ma võrdleksin külma voolu kutsumisega nendes. toetus. Iga helistaja kuulab automaatvastaja salvestust algusest lõpuni, kuid saate kõne katkestada, kasutades tellimuse tühistamist.
Tahan märkida, et on ka nn soojad ojad (sellist määratlust olen kohanud üliharva ja ainult välismaistes kogukondades) - see on oja, mis muundub külmast ojast kuumaks. Tekib küsimus - kus kasutada)) Toon näite praktikast.
Töötan Angulariga. Ta kasutab aktiivselt rxjs-i. Andmete serverisse jõudmiseks eeldan külma voogu ja kasutan seda voogu mallis asyncPipe'i abil. Kui ma seda toru mitu korda kasutan, siis külma voo definitsiooni juurde naastes küsib iga toru serverilt andmeid, mis on pehmelt öeldes kummaline. Ja kui ma muudan külma oja soojaks, siis taotlus toimub üks kord.
Üldiselt on voogude tüübi mõistmine algajatele üsna keeruline, kuid oluline.
Ettevõtjad
return this.http.get(`${environment.apiUrl}/${this.apiUrl}/trade_companies`)
.pipe(
tap(({ data }: TradeCompanyList) => this.companies$$.next(cloneDeep(data))),
map(({ data }: TradeCompanyList) => data)
);
Operaatorid annavad meile võimaluse töötada voogudega. Need aitavad kontrollida vaadeldavas sündmusi. Vaatleme paari kõige populaarsemat ja rohkem teavet operaatorite kohta leiate kasuliku teabe linkidelt.
operaatorid
Alustame abioperaatoriga. See loob vaadeldava lihtsa väärtuse alusel.
Operaatorid-filter
Filtrioperaator, nagu nimigi ütleb, filtreerib voosignaali. Kui operaator tagastab tõese, hüppab see edasi.
Operaatorid - võtke
võta – võtab väljade arvu väärtuse, mille järel voog lõpeb.
Operaatorid-debounceTime
debounceTime - jätab kõrvale väljastatud väärtused, mis jäävad väljundandmete vahel määratud ajavahemikku - pärast ajaintervalli möödumist väljastab viimane väärtus.
const { Observable } = Rx;
const { debounceTime, take } = RxOperators;
Observable.create((observer) => {
let i = 1;
observer.next(i++);
// Испускаем значение раз в 1000мс
setInterval(() => {
observer.next(i++)
}, 1000);
// Испускаем значение раз в 1500мс
setInterval(() => {
observer.next(i++)
}, 1500);
}).pipe(
debounceTime(700), // Ожидаем 700мс значения прежде чем обработать
take(3)
);
Operaator-takeWhile
Väljastab väärtusi, kuni takeWhile tagastab false, seejärel tühistab voo tellimuse.
const { Observable } = Rx;
const { debounceTime, takeWhile } = RxOperators;
Observable.create((observer) => {
let i = 1;
observer.next(i++);
// Испускаем значение раз в 1000мс
setInterval(() => {
observer.next(i++)
}, 1000);
}).pipe(
takeWhile( producer => producer < 5 )
);
Operaatorid-kombaini Viimased
Kombineeritud operaator combiLatest on mõneti sarnane lubadusega.all. See ühendab mitu voogu üheks. Pärast seda, kui iga lõime on tekitanud vähemalt ühe kiirgamise, saame neist massiivina uusimad väärtused. Lisaks annab see pärast kombineeritud voogude mis tahes kiirgamist uusi väärtusi.
const { combineLatest, Observable } = Rx;
const { take } = RxOperators;
const observer_1 = Observable.create((observer) => {
let i = 1;
// Испускаем значение раз в 1000мс
setInterval(() => {
observer.next('a: ' + i++);
}, 1000);
});
const observer_2 = Observable.create((observer) => {
let i = 1;
// Испускаем значение раз в 750мс
setInterval(() => {
observer.next('b: ' + i++);
}, 750);
});
combineLatest(observer_1, observer_2).pipe(take(5));
Operaatorid-zip
Zip – ootab igast voost väärtust ja moodustab nende väärtuste põhjal massiivi. Kui väärtus ei pärine ühestki lõimest, siis rühma ei moodustata.
const { zip, Observable } = Rx;
const { take } = RxOperators;
const observer_1 = Observable.create((observer) => {
let i = 1;
// Испускаем значение раз в 1000мс
setInterval(() => {
observer.next('a: ' + i++);
}, 1000);
});
const observer_2 = Observable.create((observer) => {
let i = 1;
// Испускаем значение раз в 750
setInterval(() => {
observer.next('b: ' + i++);
}, 750);
});
const observer_3 = Observable.create((observer) => {
let i = 1;
// Испускаем значение раз в 500
setInterval(() => {
observer.next('c: ' + i++);
}, 500);
});
zip(observer_1, observer_2, observer_3).pipe(take(5));
Operaatorid – forkJoin
forkJoin ühendab ka lõime, kuid väljastab väärtuse alles siis, kui kõik lõimed on lõpetatud.
const { forkJoin, Observable } = Rx;
const { take } = RxOperators;
const observer_1 = Observable.create((observer) => {
let i = 1;
// Испускаем значение раз в 1000мс
setInterval(() => {
observer.next('a: ' + i++);
}, 1000);
}).pipe(take(3));
const observer_2 = Observable.create((observer) => {
let i = 1;
// Испускаем значение раз в 750
setInterval(() => {
observer.next('b: ' + i++);
}, 750);
}).pipe(take(5));
const observer_3 = Observable.create((observer) => {
let i = 1;
// Испускаем значение раз в 500
setInterval(() => {
observer.next('c: ' + i++);
}, 500);
}).pipe(take(4));
forkJoin(observer_1, observer_2, observer_3);
Operaatorid-kaart
Kaardi teisenduse operaator muudab emiteeritud väärtuse uueks.
const { Observable } = Rx;
const { take, map } = RxOperators;
Observable.create((observer) => {
let i = 1;
// Испускаем значение раз в 1000мс
setInterval(() => {
observer.next(i++);
}, 1000);
}).pipe(
map(x => x * 10),
take(3)
);
Operaatorid – jagage, puudutage
Kraanioperaator võimaldab teil teha kõrvalmõjusid, st mis tahes toiminguid, mis ei mõjuta järjestust.
Jagamisteenuse operaator saab muuta külma voo kuumaks vooks.
Operaatorid on tehtud. Liigume edasi teema juurde.
Mõeldes valjusti
Ja siis läksin teed jooma. Olen väsinud nendest näidetest 😀
Teema perekond
Teemapere on kuumade lõimede ehe näide. Need klassid on omamoodi hübriid, mis toimivad nii vaatlejana kui ka vaatlejana. Kuna teema on kuum voog, tuleb selle tellimus tühistada. Kui me räägime peamistest meetoditest, siis need on järgmised:
järgmine - uute andmete edastamine voogu
viga - viga ja lõime lõpetamine
täielik - niidi lõpp
tellida – voo tellimine
unsubscribe – tühista voo tellimine
asObservable – muutuda vaatlejaks
toPromise – muundub lubaduseks
Eraldage 4 5 tüüpi aineid.
Mõeldes valjusti
Ütlesin voos 4, aga selgus, et nad lisasid veel ühe. Nagu öeldakse, ela ja õpi.
Lihtne teema new Subject()- kõige lihtsamad ained. Loodud ilma parameetriteta. Annab edasi väärtused, mis tulid alles pärast tellimust.
BehaviorSubject new BehaviorSubject( defaultData<T> ) - minu arvates kõige levinum ainetüüp. Sisend võtab vaikeväärtuse. Salvestab alati viimase numbri andmed, mis tellimisel edastatakse. Sellel klassil on ka kasulik väärtusmeetod, mis tagastab voo praeguse väärtuse.
ReplaySubject new ReplaySubject(bufferSize?: number, windowTime?: number) - Valikuliselt võib esimese argumendina võtta väärtuste puhvri suuruse, mille see ise salvestab, ja teise korra, mille jooksul vajame muudatusi.
asünkroonsubjekt new AsyncSubject() - tellimisel ei juhtu midagi ja väärtus tagastatakse alles siis, kui see on lõpetatud. Tagastada saab ainult voo viimane väärtus.
WebSocketSubject new WebSocketSubject(urlConfigOrSource: string | WebSocketSubjectConfig<T> | Observable<T>, destination?: Observer<T>) - Dokumentatsioon sellest vaikib ja ma ise näen seda esimest korda. Kes teab millega tegeleb, kirjutage, lisame.
Pheh. Noh, oleme kaalunud kõike, mida ma täna öelda tahtsin. Loodetavasti oli see teave abiks. Kirjanduse loendit saate ise lugeda vahekaardil Kasulik teave.