Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

Hæ allir. Hafðu samband Omelnitsky Sergey. Fyrir ekki svo löngu síðan hýsti ég straum um hvarfgjarna forritun, þar sem ég talaði um ósamstillingu í JavaScript. Í dag langar mig að draga þetta efni saman.

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

En áður en við byrjum á aðalefninu þurfum við að gera kynningu. Svo skulum við byrja á skilgreiningum: hvað eru stafla og biðröð?

Stafli er safn þar sem þættirnir eru sóttir á „síðast inn, fyrst út“ LIFO grundvelli

Biðröð er safn þar sem þættirnir eru fengnir samkvæmt meginreglunni („fyrstur inn, fyrst út“ FIFO

Allt í lagi, við skulum halda áfram.

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

JavaScript er forritunarmál með einum þræði. Þetta þýðir að það hefur aðeins einn þráð af framkvæmd og einn stafla þar sem aðgerðir eru í biðröð fyrir framkvæmd. Þess vegna getur JavaScript aðeins framkvæmt eina aðgerð í einu, á meðan aðrar aðgerðir munu bíða eftir röð á staflanum þar til þær eru kallaðar.

Símtalastakki er gagnastrúktúr sem á einfaldan hátt skráir upplýsingar um staðinn í forritinu þar sem við erum. Ef við hoppum inn í fall ýtum við innkomu hennar efst í staflann. Þegar við komum til baka frá falli, skjótum við efsta þættinum úr staflanum og endum þar sem við kölluðum þetta fall frá. Það er allt sem staflinn getur gert. Og nú mjög áhugaverð spurning. Hvernig virkar þá ósamstilling í JavasScript?

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

Reyndar, auk staflans, hafa vafrar sérstaka biðröð til að vinna með svokallaða WebAPI. Aðgerðir úr þessari biðröð verða framkvæmdar í röð eftir að staflan er alveg hreinsuð. Aðeins eftir það eru þeir settir úr röðinni á stafla til framkvæmdar. Ef það er að minnsta kosti einn þáttur á bunkanum í augnablikinu, þá komast þau ekki á bunkann. Bara vegna þessa er oft ónákvæmt að kalla aðgerðir eftir tímamörkum í tíma, þar sem aðgerðin kemst ekki úr biðröðinni í stafla á meðan hún er full.

Við skulum skoða eftirfarandi dæmi og fara í gegnum það skref fyrir skref. Við skulum líka sjá hvað gerist í kerfinu.

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

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

1) Ekkert er að gerast ennþá. Vafraborðið er hreint, símtalastaflinn er tómur.

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

2) Síðan er skipuninni console.log('Hi') bætt við kallstaflann.

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

3) Og það er uppfyllt

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

4) Þá er console.log('Hi') fjarlægður úr símtalstaflanum.

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

5) Nú skulum við halda áfram að skipuninni setTimeout(fall cb1() {… }). Það er bætt við símtalabunkann.

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

6) Skipunin setTimeout(fall cb1() {… }) er keyrð. Vafrinn býr til tímamæli sem er hluti af vef API. Það mun framkvæma niðurtalningu.

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

7) Skipunin setTimeout(fall cb1() {… }) hefur lokið verki sínu og er fjarlægð úr kallastaflanum.

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

8) console.log('Bye') skipuninni er bætt við kallstaflann.

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

9) Skipunin console.log('Bye') er keyrð.

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

10) Skipunin console.log('Bye') er fjarlægð úr símtalsbunkanum.

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

11) Eftir að að minnsta kosti 5000 ms eru liðnar lýkur tímamælirinn og setur cb1 svarhringinguna í svarhringingarröðina.

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

12) Atburðalykkjan tekur fall cb1 úr svarhringingarröðinni og ýtir henni inn í símtalabunkann.

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

13) Cb1 aðgerðin er keyrð og bætir console.log('cb1') við kallstaflann.

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

14) Skipunin console.log('cb1') er keyrð.

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

15) Skipunin console.log('cb1') er fjarlægð úr kallastaflanum.

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

16) Aðgerð cb1 er fjarlægð úr símtalsbunkanum.

Við skulum skoða dæmi í gangfræði:

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

Jæja, við skoðuðum hvernig ósamstilling er útfærð í JavaScript. Nú skulum við tala stuttlega um þróun ósamstilltra kóða.

Þróun ósamstilltra kóða.

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

Ósamstillt forritun eins og við þekkjum hana í JavaScript er aðeins hægt að útfæra með aðgerðum. Hægt er að senda þær eins og allar aðrar breytur yfir í aðrar aðgerðir. Svona fæddust hringingar. Og það er flott, skemmtilegt og fjörugt, þar til það breytist í sorg, depurð og sorg. Hvers vegna? Það er einfalt:

  • Eftir því sem flókinn kóðann eykst breytist verkefnið fljótt í óljósar margar hreiðrar blokkir - „tilbakahringingarhelvíti“.
  • Auðvelt er að líta framhjá villumeðferð.
  • Þú getur ekki skilað tjáningum með return.

Með tilkomu Promise hefur ástandið orðið aðeins betra.

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

  • Loforðakeðjur komu fram sem bættu læsileika kóðans
  • Það var sérstök aðferð til að hlera villur
  • Samhliða framkvæmd með Promise.all bætt við
  • Við getum leyst hreiður ósamstilling með því að nota ósamstillingu/bíður

En loforðið hefur sínar takmarkanir. Til dæmis er ekki hægt að hætta við loforð, án þess að dansa við bumbur, og síðast en ekki síst, það virkar með einu gildi.

Jæja, hér erum við að nálgast hvarfgjarna forritun. Þreyttur? Jæja, það góða er að þú getur farið að brugga nokkra máva, heilastormað og farið aftur til að lesa meira. Og ég mun halda áfram.

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

Reactive forritun - forritunaraðferð sem beinist að gagnaflæði og útbreiðslu breytinga. Við skulum skoða nánar hvað gagnastraumur er.

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

const eventsArray = [];

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

Ímyndum okkur að við höfum innsláttarreit. Við búum til fylki og fyrir hverja takkauppfærslu inntaksviðburðarins munum við geyma atburðinn í fylkinu okkar. Jafnframt vil ég taka fram að fylkið okkar er raðað eftir tíma, þ.e. vísitala síðari atburða er hærri en vísitala fyrri atburða. Slík fylki er einfaldað gagnaflæðislíkan, en það er ekki enn flæði. Til þess að þetta fylki sé örugglega kallað straumur verður það að geta á einhvern hátt upplýst áskrifendur um að ný gögn séu komin í hann. Þannig komum við að skilgreiningunni á flæði.

Gagnastraumur

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

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

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

Rennsli er gagnaflokkur flokkaður eftir tíma sem getur gefið til kynna að gögnin hafi breyst. Ímyndaðu þér núna hversu þægilegt það verður að skrifa kóða þar sem þú þarft að kalla fram nokkra atburði í mismunandi hlutum kóðans fyrir eina aðgerð. Við gerumst einfaldlega áskrifendur að straumnum og hann mun segja okkur hvenær breytingar verða. Og RxJs bókasafnið getur gert þetta.

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

RxJS er bókasafn til að vinna með ósamstillt og atburðabundið forrit með því að nota sjáanlegar raðir. Bókasafnið veitir aðalgerðina Athuganlegt, nokkrar hjálpargerðir (Áheyrnarfulltrúar, dagskrárgerðarmenn, viðfangsefni) og rekstraraðila til að vinna með viðburði eins og með söfn (korta, sía, minnka, hvert og svipaðar frá JavaScript Array).

Við skulum skilja grunnhugtök þessa bókasafns.

Áberandi, áhorfandi, framleiðandi

Athuganleg er fyrsta grunntegundin sem við munum skoða. Þessi flokkur inniheldur meginhluta RxJs útfærslunnar. Það er tengt straumi sem hægt er að fylgjast með, sem hægt er að gerast áskrifandi að með áskriftaraðferðinni.

Observable útfærir hjálparkerfi til að búa til uppfærslur, svokallaða Observer. Uppspretta gilda fyrir áheyrnarfulltrúa er kallað Leikstjóri. Það getur verið fylki, endurtekning, veftengi, einhvers konar atburður osfrv. Svo við getum sagt að observable sé leiðari milli framleiðanda og áheyrnarfulltrúa.

Observable sér um þrjár tegundir af Observer atburðum:

  • næsta - ný gögn
  • villa – villa ef röðinni lauk vegna undantekningar. þessi atburður felur einnig í sér að röðinni sé lokið.
  • lokið — merki um að röðinni sé lokið. Þetta þýðir að ekki verða fleiri ný gögn.

Við skulum sjá kynningu:

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

Í upphafi munum við vinna úr gildunum 1, 2, 3 og eftir 1 sek. við fáum 4 og endum þráðinn okkar.

Að hugsa upphátt

Og svo áttaði ég mig á því að það var áhugaverðara að segja frá en að skrifa um það. 😀

Áskrift

Þegar við gerumst áskrifendur að straumi búum við til nýjan flokk Áskrift, sem gefur okkur möguleika á að segja upp áskrift með aðferðinni afskrá. Við getum líka flokkað áskriftir með aðferðinni bæta við. Jæja, það er rökrétt að við getum sundrað þræði með því að nota fjarlægja. Bæta við og fjarlægja aðferðir samþykkja aðra áskrift sem inntak. Ég vil taka það fram að þegar við segjum upp áskrift þá afskrifum við allar barnaáskriftir eins og þær kölluðu líka afskráningaraðferðina. Gjörðu svo vel.

Tegundir strauma

HOT
KALT

Framleiðandi er skapaður utan hins sjáanlega
Framleiðandi er búin til inni sjáanleg

Gögn eru send á þeim tíma sem sjáanlegt er búið til
Gögn eru veitt við áskrift.

Þarf meiri rökfræði til að segja upp áskrift
Þráðurinn lýkur af sjálfu sér

Notar eitt-á-marga samband
Notar einstaklingssamband

Allar áskriftir hafa sömu merkingu
Áskriftir eru sjálfstæðar

Gögn geta glatast ef engin áskrift er
Endurútgefur öll straumgildi fyrir nýja áskrift

Til að gefa samlíkingu myndi ég ímynda mér heitan straum eins og kvikmynd í kvikmyndahúsi. Á hvaða tímapunkti þú komst, frá þeirri stundu byrjaðir þú að horfa. Ég myndi bera saman kalt straum við símtal í þeim. stuðning. Allir sem hringja hlusta á upptöku símsvarans frá upphafi til enda, en þú getur lagt á með afskráningu.

Ég vil taka það fram að það eru líka svokallaðir hlýir lækir (ég hef hitt slíka skilgreiningu afar sjaldan og aðeins í erlendum samfélögum) - þetta er lækur sem breytist úr köldum straumi í heitan. Spurningin vaknar - hvar á að nota)) Ég mun gefa dæmi frá æfingum.

Ég er að vinna með Angular. Hann notar rxjs virkan. Til að fá gögn á netþjóninn býst ég við köldum straumi og ég nota þennan straum í sniðmátinu með asyncPipe. Ef ég nota þessa pípu nokkrum sinnum, þá, aftur í skilgreininguna á köldum straumi, mun hver pípa biðja um gögn frá þjóninum, sem er vægast sagt skrítið. Og ef ég breyti köldum straumi í heitan, þá mun beiðnin gerast einu sinni.

Almennt séð er nokkuð erfitt fyrir byrjendur að skilja tegund flæðis, en mikilvægt.

Stjórnandi

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

Rekstraraðilar veita okkur tækifæri til að vinna með strauma. Þeir hjálpa til við að stjórna atburðunum sem streyma í Observable. Við munum íhuga nokkra af þeim vinsælustu og frekari upplýsingar um rekstraraðilana er að finna á krækjunum í gagnlegum upplýsingum.

Rekstraraðilar-af

Við skulum byrja með hjálpar rekstraraðila. Það býr til Observable byggt á einföldu gildi.

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

Rekstraraðilar - sía

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

Síustjórinn, eins og nafnið gefur til kynna, síar straummerkið. Ef stjórnandinn skilar satt, þá sleppur hann lengra.

Rekstraraðilar - taka

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

taka - Tekur gildi fjölda losunar, eftir það lýkur straumnum.

Operators-debounceTime

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

debounceTime - fleygir útsendum gildum sem falla innan tilgreinds tímabils milli úttaksgagna - eftir að tímabilið er liðið, gefur frá sér síðasta gildið.

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

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

Rekstraraðilar-taka á meðan

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

Sendir gildi þar til takeWhile skilar false, eftir það segir það upp áskrift að þræðinum.

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

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

Operators-combineNýjasta

Sameinaði rekstraraðilinn combineLatest er nokkuð svipaður og lofa.allt. Það sameinar marga strauma í einn. Eftir að hver þráður hefur gefið að minnsta kosti eina útsendingu fáum við nýjustu gildin frá hverjum sem fylki. Ennfremur, eftir hvers kyns losun frá sameinuðu straumunum, mun það gefa ný gildi.

Ósamstilltur forritun í 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));

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

Operators-zip

Zip - bíður eftir gildi úr hverjum straumi og myndar fylki sem byggir á þessum gildum. Ef gildið kemur ekki frá neinum þræði, þá verður hópurinn ekki myndaður.

Ósamstilltur forritun í 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));

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

Rekstraraðilar - forkJoin

forkJoin tengir einnig þræði, en það gefur aðeins frá sér gildi þegar allir þræðir eru búnir.

Ósamstilltur forritun í 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);

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

Rekstrarkort

Stjórnandi kortabreytingar umbreytir losunargildinu í nýtt.

Ósamstilltur forritun í 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)
);

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

Rekstraraðilar - deildu, pikkaðu á

Kranatæki gerir þér kleift að gera aukaverkanir, það er að segja allar aðgerðir sem hafa ekki áhrif á röðina.

Rekstraraðili hlutaveitunnar getur breytt köldum straumi í heitan.

Ósamstilltur forritun í JavaScript (Callback, Promise, RxJs)

Rekstraraðilar eru búnir. Við skulum halda áfram í Subject.

Að hugsa upphátt

Og svo fór ég að drekka te. Ég er þreytt á þessum dæmum 😀

Efnisfjölskylda

Viðfangsefnafjölskyldan er gott dæmi um heita þræði. Þessir flokkar eru eins konar blendingar sem virka sem áhorfandi og áhorfandi á sama tíma. Þar sem viðfangsefnið er heitur straumur verður að segja upp áskrift að því. Ef við tölum um helstu aðferðir, þá eru þessar:

  • næst - að senda ný gögn til straumsins
  • villa - villa og þráðarslit
  • lokið - enda þráðarins
  • gerast áskrifandi - gerast áskrifandi að straumi
  • afskrá - afskráðu þig af straumnum
  • asobservable - umbreytast í áhorfanda
  • toPromise - breytist í loforð

Úthlutaðu 4 5 tegundum viðfangsefna.

Að hugsa upphátt

Ég sagði 4 á straumnum, en það kom í ljós að þeir bættu einum við. Eins og orðatiltækið segir, lifðu og lærðu.

Einfalt efni new Subject()- einfaldasta tegund viðfangsefna. Búið til án breytu. Stendur gildin sem komu aðeins eftir áskriftina.

Hegðun Viðfangsefni new BehaviorSubject( defaultData<T> ) - að mínu mati algengasta tegund námsgreina. Inntakið tekur sjálfgefið gildi. Vistar alltaf gögn síðasta tölublaðs, sem eru send við áskrift. Þessi flokkur hefur einnig gagnlega gildisaðferð sem skilar núverandi gildi straumsins.

EndurspilunSubject new ReplaySubject(bufferSize?: number, windowTime?: number) - Valfrjálst getur það tekið sem fyrstu röksemd stærðar biðminni gilda sem það mun geyma í sjálfu sér og í seinna skiptið þar sem við þurfum breytingar.

ósamstillt viðfangsefni new AsyncSubject() - ekkert gerist þegar þú gerist áskrifandi og gildið verður aðeins skilað þegar því er lokið. Aðeins síðasta gildi straumsins verður skilað.

WebSocketSubject new WebSocketSubject(urlConfigOrSource: string | WebSocketSubjectConfig<T> | Observable<T>, destination?: Observer<T>) - Skjölin eru þögul um það og ég sé það sjálfur í fyrsta skipti. Hver veit hvað hann gerir, skrifaðu, bætum við.

Púff. Jæja, við höfum íhugað allt sem ég vildi segja í dag. Vona að þessar upplýsingar hafi verið gagnlegar. Þú getur lesið listann yfir bókmenntir á eigin spýtur í flipanum Gagnlegar upplýsingar.

gagnlegar upplýsingar

Heimild: www.habr.com

Bæta við athugasemd