Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

Hoi allegearre. Yn kontakt Omelnitsky Sergey. Net sa lang lyn host ik in stream oer reaktive programmearring, wêr't ik praat oer asynchrony yn JavaScript. Hjoed wol ik dit materiaal gearfetsje.

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

Mar foardat wy it haadmateriaal begjinne, moatte wy in ynlieding meitsje. Dus litte wy begjinne mei definysjes: wat binne stack en wachtrige?

Steapelje is in kolleksje wêrfan de eleminten wurde ophelle op in "lêst yn, earst út" LIFO basis

Rigel is in kolleksje wêrfan de eleminten wurde krigen neffens it prinsipe ("earst yn, earst út" FIFO

Okee, litte wy trochgean.

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

JavaSkript is in single-threaded programmeartaal. Dit betsjut dat it mar ien tried fan útfiering hat en ien stapel wêr't funksjes yn wachtrige steane foar útfiering. Dêrom kin JavaScript mar ien operaasje tagelyk útfiere, wylst oare operaasjes har beurt op 'e stapel wachtsje oant se oproppen wurde.

Call stack is in gegevensstruktuer dy't, yn ienfâldige termen, ynformaasje registrearret oer it plak yn it programma wêr't wy binne. As wy springe yn in funksje, wy triuwe syn yngong nei de top fan 'e stapel. As wy weromkomme fan in funksje, popje wy it boppeste elemint út 'e stapel en einigje wêr't wy dizze funksje neamden. Dat is alles wat de stapel kin dwaan. En no in tige nijsgjirrige fraach. Hoe wurket asynchrony dan yn JavasScript?

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

Yn feite, neist de stack, browsers hawwe in spesjale wachtrige foar it wurkjen mei de saneamde WebAPI. Funksjes fan dizze wachtrige wurde allinich yn oarder útfierd nei't de stapel folslein wiske is. Pas dêrnei wurde se út 'e wachtrige op' e steapel pleatst foar útfiering. As der op it stuit op syn minst ien elemint op 'e steapel is, dan kinne se net op' e steapel komme. Krekt hjirtroch is it oproppen fan funksjes troch time-out faak net krekt yn 'e tiid, om't de funksje net kin komme fan' e wachtrige nei de stapel wylst it fol is.

Litte wy nei it folgjende foarbyld sjen en litte wy it stap foar stap troch gean. Litte wy ek sjen wat der bart yn it systeem.

console.log('Hi');
setTimeout(function cb1() {
    console.log('cb1');
}, 5000);
console.log('Bye');

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

1) Oant no bart der neat. De browserkonsole is skjin, de opropstap is leech.

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

2) Dan wurdt it kommando console.log('Hi') tafoege oan de opropstapel.

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

3) En it is folbrocht

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

4) Dan wurdt console.log('Hi') fuortsmiten fan 'e opropstapel.

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

5) Litte wy no trochgean nei it kommando setTimeout (funksje cb1() {... }). It wurdt tafoege oan de oprop stack.

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

6) It kommando setTimeout(funksje cb1() {... }) wurdt útfierd. De browser makket in timer dy't diel útmakket fan 'e Web API. It sil in countdown útfiere.

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

7) It kommando setTimeout(funksje cb1() {... }) hat syn wurk foltôge en wurdt fuortsmiten fan de opropstapel.

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

8) It kommando console.log('Bye') wurdt tafoege oan de opropstapel.

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

9) It kommando console.log('Bye') wurdt útfierd.

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

10) It kommando console.log('Bye') wurdt fuortsmiten fan de opropstapel.

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

11) Nei't op syn minst 5000ms binne ferrûn, einiget de timer en set de cb1 werombel yn 'e werombelwachtrige.

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

12) De barrenslus nimt funksje cb1 út 'e callback-wachtrige en triuwt it op 'e opropstapel.

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

13) De funksje cb1 wurdt útfierd en foeget console.log('cb1') ta oan de opropstapel.

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

14) It kommando console.log('cb1') wurdt útfierd.

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

15) It kommando console.log('cb1') wurdt fuortsmiten fan de opropstapel.

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

16) Funksje cb1 wurdt fuorthelle út de oprop stack.

Litte wy nei in foarbyld sjen yn dynamyk:

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

No, wy seagen hoe't asynchrony wurdt ymplementearre yn JavaScript. No litte wy koart prate oer de evolúsje fan asynchrone koade.

De evolúsje fan asynchrone koade.

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);
                    })
                })
            })
        })
    })
});

Asynchronous programmearring sa't wy it kenne yn JavaScript kin allinich dien wurde mei funksjes. Se kinne wurde trochjûn lykas elke oare fariabele nei oare funksjes. Dit is hoe't callbacks binne berne. En it is koel, leuk en fûleindich, oant it feroaret yn fertriet, weemoed en fertriet. Wêrom? Ja, it is ienfâldich:

  • As de kompleksiteit fan 'e koade groeit, feroaret it projekt fluch yn obskure meardere nestele blokken - "callback hel".
  • Flater ôfhanneling kin maklik oersjoen.
  • Jo kinne net werom útdrukkings mei werom.

Mei de komst fan Promise is de situaasje wat better wurden.

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);
});

  • Promise keatlingen ferskynden, dy't de lêsberens fan 'e koade ferbettere
  • Der wie in aparte metoade foar ûnderskepping fan flaters
  • Parallelle útfiering mei Promise.all tafoege
  • Wy kinne geneste asynchrony oplosse mei async / wachtsje

Mar de belofte hat syn beheiningen. Bygelyks, in belofte, sûnder dûnsjen mei in tamboerine, kin net annulearre wurde, en it wichtichste, it wurket mei ien wearde.

No, hjir komme wy soepel oan by reaktive programmearring. Wurch? No, it goede ding is dat jo kinne gean om wat gulls te brouwen, brainstoarmje en weromkomme om mear te lêzen. En ik sil trochgean.

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

Reaktive programmearring - in programmearparadigma rjochte op gegevensstreamen en de fersprieding fan feroaringen. Litte wy in tichterby besjen wat in gegevensstream is.

// Получаем ссылку на элемент
const input = ducument.querySelector('input');

const eventsArray = [];

// Пушим каждое событие в массив eventsArray
input.addEventListener('keyup',
    event => eventsArray.push(event)
);

Litte wy ús foarstelle dat wy in ynfierfjild hawwe. Wy meitsje in array, en foar elke keyup fan it ynfierevenemint sille wy it barren opslaan yn ús array. Tagelyk wol ik opmerke dat ús array op tiid sortearre is, d.w.s. de yndeks fan lettere foarfallen is grutter as de yndeks fan eardere foarfallen. Sa'n array is in ferienfâldige datastreammodel, mar it is noch gjin stream. Om dizze array feilich in stream te neamen, moat it abonnees op ien of oare manier ynformearje kinne dat nije gegevens deryn binne oankommen. Sa komme wy by de definysje fan stream.

Datastream

const { interval } = Rx;
const { take } = RxOperators;

interval(1000).pipe(
    take(4)
)

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

Streame is in array fan gegevens sortearre op tiid dat kin oanjaan dat de gegevens binne feroare. Stel jo no foar hoe handich it wurdt om koade te skriuwen wêryn jo ferskate eveneminten moatte triggerje yn ferskate dielen fan 'e koade foar ien aksje. Wy abonnearje gewoan op 'e stream en it sil ús fertelle wannear't feroaringen foarkomme. En de RxJs-bibleteek kin dit dwaan.

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

RxJS is in bibleteek foar wurkjen mei asynchrone en evenemint-basearre programma mei help fan waarneembare sekwinsjes. De biblioteek leveret it haadtype Observearber, ferskate helpertypen (Observers, Schedulers, Underwerpen) en operators foar it wurkjen mei eveneminten lykas mei kolleksjes (map, filter, ferminderje, eltse en ferlykbere út JavaScript Array).

Litte wy de basisbegripen fan dizze bibleteek begripe.

Observable, Observer, Producer

Observable is it earste basistype dat wy sille sjen. Dizze klasse befettet it haaddiel fan 'e RxJs ymplemintaasje. It is assosjearre mei in waarneembare stream, dêr't kin wurde ynskreaun mei de subscribe metoade.

Observable ymplemintearret in auxiliary meganisme foar it meitsjen fan updates, de saneamde observer. De boarne fan wearden foar in Observer wurdt neamd Produsint. It kin in array wêze, in iterator, in websocket, in soarte fan evenemint, ensfh. Sa kinne wy ​​sizze dat observable is in dirigint tusken Produsint en Observer.

Observable behannelet trije soarten Observer-eveneminten:

  • folgjende - nije gegevens
  • flater - in flater as de folchoarder beëinige is fanwege in útsûndering. dit barren ymplisearret ek de ein fan 'e folchoarder.
  • kompleet - in sinjaal oer de ein fan 'e folchoarder. Dit betsjut dat der gjin nije gegevens mear komme

Litte wy in demo sjen:

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

Oan it begjin ferwurkje wy de wearden 1, 2, 3, en nei 1 sek. wy krije 4 en einigje ús tried.

Hardop tinken

En doe besefte ik dat it nijsgjirriger wie om te fertellen as der oer te skriuwen. 😀

Ynskriuwing

As wy abonnearje op in stream, meitsje wy in nije klasse ynskriuwing, dy't ús de opsje jout om mei de metoade ôf te melden ôfskriuw. Wy kinne ek abonneminten groepearje mei de metoade add. No, it is logysk dat wy diskusjes kinne degroepearje mei help fan weinimme. De metoaden tafoegje en fuortsmite akseptearje in oar abonnemint as ynfier. Ik soe graach opmerke dat as wy útskriuwe, wy ôfmelde foar alle bern abonneminten as soe se ek neamd de útskriuwe metoade. Gean dyn gong.

Soarten streamen

HYT
KJELD

Produsint wurdt makke bûten it waarneembare
Produsint wurdt makke binnen observable

Gegevens wurde trochjûn op it momint dat de waarneembare wurdt makke
Gegevens wurde levere op it momint fan abonnemint.

Mear logika nedich om útskriuwe
De tried einiget op himsels

Brûkt in ien-op-in protte relaasje
Brûkt in ien-op-ien relaasje

Alle abonneminten hawwe deselde wearde
Abonneminten binne ûnôfhinklik

Gegevens kinne ferlern gean as der gjin abonnemint is
Jout alle streamwearden opnij út foar in nij abonnemint

Om in analogy te jaan, soe ik my in hite stream foarstelle as in film yn in bioskoop. Op hokker tiidstip kaamst, fan dat momint ôf begûnst te sjen. Ik soe ferlykje in kâlde stream mei in oprop yn dy. stypje. Elke beller harket nei de opname fan it antwurdapparaat fan begjin oant ein, mar jo kinne ophingje mei ôfmelde.

Ik wol opmerke dat d'r ek saneamde waarme streamen binne (ik haw sa'n definysje ekstreem selden en allinich yn bûtenlânske mienskippen moete) - dit is in stream dy't fan in kâlde stream yn in waarme feroaret. De fraach ûntstiet - wêr te brûken)) Ik sil in foarbyld jaan út 'e praktyk.

Ik wurkje mei Angular. Hy brûkt aktyf rxjs. Om gegevens nei de tsjinner te krijen, ferwachtsje ik in kâlde stream en ik brûk dizze stream yn 'e sjabloan mei asyncPipe. As ik dizze piip ferskate kearen brûke, dan, werom nei de definysje fan in kâlde stream, sil elke piip gegevens fan 'e tsjinner freegje, wat op syn minst frjemd is. En as ik in kâlde stream omsette yn in waarme, dan komt it fersyk ien kear.

Yn 't algemien is it begripen fan it type streamen frij lestich foar begjinners, mar wichtich.

Oanbieders

return this.http.get(`${environment.apiUrl}/${this.apiUrl}/trade_companies`)
    .pipe(
        tap(({ data }: TradeCompanyList) => this.companies$$.next(cloneDeep(data))),
        map(({ data }: TradeCompanyList) => data)
    );

Operators jouwe ús in kâns om te wurkjen mei streamen. Se helpe by it kontrolearjen fan de eveneminten dy't streame yn 'e Observable. Wy sille in pear fan 'e populêrste beskôgje, en mear ynformaasje oer de operators is te finen by de keppelings yn nuttige ynformaasje.

Operators-fan

Litte wy begjinne mei de helper operator fan. It makket in Observable basearre op in ienfâldige wearde.

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

Operators-filter

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

De filteroperator, lykas de namme al fermoeden docht, filteret it streamsinjaal. As de operator wier weromkomt, dan springt it fierder.

Operators - nimme

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

take - Nimt de wearde fan it oantal útstjit, wêrnei't de stream einiget.

Operators-debounceTime

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

debounceTime - smyt útstjoerde wearden dy't binnen it opjûne tiidynterval tusken útfiergegevens falle - nei it tiidynterval is foarby, stjoert de lêste wearde út.

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)
);  

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

Operators-takeWhile

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

Stjoert wearden út oant takeWhile falsk weromkomt, dan ôfskriuwt de thread.

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 )
);  

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

Operators-combineLatest

De kombinearre operator combineLatest is wat ferlykber mei belofte.all. It kombinearret meardere streamen yn ien. Nei't elke thread op syn minst ien emit hat makke, krije wy de lêste wearden fan elk as in array. Fierder sil it nei elke útstjit fan 'e kombineare streamen nije wearden jaan.

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

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));

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

Operators-zip

Zip - wachtet op in wearde fan elke stream en foarmet in array basearre op dizze wearden. As de wearde net komt fan in tried, dan sil de groep net wurde foarme.

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

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));

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

Operators - forkJoin

forkJoin docht ek mei oan diskusjes, mar it stjoert allinich in wearde út as alle triedden foltôge binne.

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

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);

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

Operators-map

De operator foar kaarttransformaasje transformeart de emitwearde yn in nije.

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

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)
);

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

Operators - diele, tap

De tapoperator lit jo side-effekten dwaan, dat is alle aksjes dy't de folchoarder net beynfloedzje.

De operator fan it share utility kin in kâlde stream omsette yn in waarme.

Asynchronous programmearring yn JavaScript (Callback, Promise, RxJs)

Operators binne dien. Litte wy trochgean nei Underwerp.

Hardop tinken

En doe gong ik tee drinke. Ik bin nocht fan dizze foarbylden 😀

Underwerp famylje

De ûnderwerpfamylje is in prima foarbyld fan waarme triedden. Dizze klassen binne in soarte fan hybride dy't tagelyk fungearje as waarnimmer en waarnimmer. Om't it ûnderwerp in heule stream is, moat it útskreaun wurde. As wy prate oer de wichtichste metoaden, dan binne dit:

  • folgjende - trochjaan fan nije gegevens nei de stream
  • flater - flater en thread beëiniging
  • kompleet - ein fan 'e tried
  • abonnearje - abonnearje op in stream
  • ôfmelde - ôfmelde by de stream
  • asObservable - transformearje yn in waarnimmer
  • toPromise - feroaret yn in belofte

Tawize 4 5 soarten ûnderwerpen.

Hardop tinken

Ik sei 4 op 'e stream, mar it die bliken dat se tafoege ien mear. As it sprekwurd seit, libje en leare.

Ienfâldich ûnderwerp new Subject()- de ienfâldichste soarte fan ûnderwerpen. Makke sûnder parameters. Past de wearden troch dy't pas nei it abonnemint kamen.

GedrachSubject new BehaviorSubject( defaultData<T> ) - neffens my de meast foarkommende soarte fan ûnderwerpen. De ynfier nimt de standertwearde. Slaat altyd de gegevens fan 'e lêste útjefte op, dy't by it ynskriuwen oerdroegen wurdt. Dizze klasse hat ek in brûkbere wearde metoade dy't jout de hjoeddeiske wearde fan de stream.

ReplaySubject new ReplaySubject(bufferSize?: number, windowTime?: number) - Opsjoneel kin it as earste argumint de grutte fan 'e buffer fan wearden nimme dy't it yn himsels sil opslaan, en de twadde kear wêryn't wy wizigingen nedich binne.

asynk ûnderwerp new AsyncSubject() - der bart neat by it ynskriuwen, en de wearde sil allinich weromjûn wurde as it is foltôge. Allinnich de lêste wearde fan 'e stream sil weromjûn wurde.

WebSocketSubject new WebSocketSubject(urlConfigOrSource: string | WebSocketSubjectConfig<T> | Observable<T>, destination?: Observer<T>) - De dokumintaasje swijt der oer en ik sjoch it sels foar it earst. Wa wit wat er docht, skriuw, sille wy tafoegje.

Pff. No, wy hawwe alles betocht wat ik hjoed fertelle woe. Hoopje dat dizze ynformaasje wie nuttich. Jo kinne de list mei literatuer op jo eigen lêze yn it ljepblêd Nuttige ynformaasje.

helpful ynformaasje

Boarne: www.habr.com

Add a comment