Asynkron programmering i JavaScript (Callback, Promise, RxJs)

Hei alle sammen. Sergey Omelnitsky tar kontakt. For ikke lenge siden var jeg vert for en strøm om reaktiv programmering, hvor jeg snakket om asynkroni i JavaScript. I dag vil jeg ta notater om dette materialet.

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

Men før vi starter hovedmaterialet, må vi lage et innledende notat. Så la oss starte med definisjoner: hva er en stabel og en kø?

Stable er en samling hvis elementer er hentet på en sist inn, først ut LIFO basis

er en samling hvis elementer er hentet på en først inn, først ut FIFO basis

Ok, la oss fortsette.

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

JavaScript er et enkelt-tråds programmeringsspråk. Dette betyr at det kun er én utførelsestråd og én stabel der funksjoner står i kø for utførelse. Derfor kan JavaScript bare utføre én operasjon om gangen, mens andre operasjoner vil vente på sin tur på stabelen til de blir kalt.

Ring stabel er en datastruktur som enkelt sagt registrerer informasjon om stedet i programmet der vi er. Hvis vi går inn i en funksjon, skyver vi inngangen til toppen av stabelen. Når vi kommer tilbake fra en funksjon, spretter vi det øverste elementet fra stabelen og ender tilbake der vi kalte funksjonen. Dette er alt stabelen kan gjøre. Og nå et ekstremt interessant spørsmål. Hvordan fungerer da asynkroni i JavasScript?

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

Faktisk, i tillegg til stabelen, har nettlesere en spesiell kø for å jobbe med den såkalte WebAPI. Funksjonene i denne køen vil bli utført i rekkefølge først etter at stabelen er fullstendig tømt. Først etter dette blir de skjøvet fra køen til stabelen for utførelse. Hvis det er minst ett element på stabelen for øyeblikket, kan de ikke legges til stabelen. Det er nettopp på grunn av dette at det å kalle funksjoner etter timeout ofte ikke er presist i tid, siden funksjonen ikke kan komme fra køen til stabelen mens den er full.

La oss se på følgende eksempel og komme i gang med trinn-for-trinn-implementeringen. La oss også se hva som skjer i systemet.

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

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

1) Ingenting skjer ennå. Nettleserkonsollen er klar, anropsstakken er tom.

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

2) Deretter legges kommandoen console.log('Hi') til anropsstakken.

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

3) Og det er oppfylt

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

4) Deretter fjernes console.log('Hi') fra anropsstakken.

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

5) Gå nå videre til kommandoen setTimeout(funksjon cb1() {... }). Den legges til anropsstakken.

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

6) Kommandoen setTimeout(function cb1() {... }) utføres. Nettleseren lager en tidtaker som er en del av Web API. Den vil utføre en nedtelling.

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

7) Kommandoen setTimeout(function cb1() {... }) har fullført arbeidet og er fjernet fra anropsstakken.

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

8) Kommandoen console.log('Bye') legges til anropsstakken.

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

9) Kommandoen console.log('Bye') utføres.

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

10) Kommandoen console.log('Bye') fjernes fra anropsstakken.

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

11) Etter at minst 5000 ms har gått, avsluttes timeren og plasserer tilbakeringing cb1 i tilbakeringingskøen.

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

12) Hendelsesløkken tar funksjon cb1 fra tilbakeringingskøen og plasserer den på anropsstakken.

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

13) Funksjonen cb1 utføres og legger til console.log('cb1') til anropsstakken.

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

14) Kommandoen console.log('cb1') utføres.

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

15) Kommandoen console.log('cb1') fjernes fra anropsstakken.

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

16) Funksjon cb1 fjernes fra anropsstakken.

La oss se på et eksempel innen dynamikk:

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

Vel, vi så på hvordan asynkroni er implementert i JavaScript. La oss nå snakke kort om utviklingen av asynkron kode.

Utviklingen av asynkron kode.

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

Asynkron programmering slik vi kjenner det i JavaScript kan bare implementeres av funksjoner. De kan overføres som alle andre variabler til andre funksjoner. Dette er hvordan tilbakeringinger ble født. Og det er kult, morsomt og lekent, helt til det blir til tristhet, melankoli og tristhet. Hvorfor? Det er enkelt:

  • Etter hvert som kompleksiteten til koden øker, blir prosjektet raskt til obskure, gjentatte nestede blokker - "callback hell".
  • Feilhåndtering kan være lett å gå glipp av.
  • Du kan ikke returnere uttrykk med retur.

Med fremkomsten av Promise ble situasjonen litt bedre.

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

  • Løftekjeder dukket opp, noe som forbedret kodelesbarheten
  • En egen metode for å fange opp feil har dukket opp
  • Lagt til muligheten for parallell utførelse ved hjelp av Promise.all
  • Vi kan løse nestet asynkroni ved å bruke async/wait

Men løfter har sine begrensninger. For eksempel kan et løfte ikke kanselleres uten å danse med en tamburin, og det viktigste er at det fungerer med én verdi.

Vel, vi har lett nærmet oss reaktiv programmering. Trett? Vel, heldigvis kan du gå og lage te, tenke på det og komme tilbake for å lese mer. Og jeg vil fortsette.

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

Reaktiv programmering er et programmeringsparadigme fokusert på dataflyter og endringsforplantning. La oss se nærmere på hva en datastrøm er.

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

const eventsArray = [];

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

La oss tenke oss at vi har et inndatafelt. Vi oppretter en matrise og for hver tasting av inndatahendelsen vil vi lagre hendelsen i matrisen vår. Samtidig vil jeg merke meg at arrayet vårt er sortert etter tid, dvs. indeksen over senere hendelser er større enn indeksen over tidligere. En slik matrise er en forenklet modell av en dataflyt, men den er ennå ikke en flyt. For at denne matrisen trygt skal kunne kalles en strøm, må den på en eller annen måte kunne informere abonnenter om at nye data har kommet inn i den. Dermed kommer vi til definisjonen av flyt.

Data strøm

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

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

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

Strømme er en rekke data sortert etter tid som kan indikere at dataene har endret seg. Tenk deg nå hvor praktisk det blir å skrive kode der én handling krever å kalle opp flere hendelser i forskjellige deler av koden. Vi abonnerer ganske enkelt på strømmen, og den vil varsle oss når endringer skjer. Og RxJs-biblioteket kan gjøre dette.

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

RxJS er et bibliotek for arbeid med asynkrone og hendelsesbaserte programmer ved bruk av observerbare sekvenser. Biblioteket gir en grunnleggende type observer, flere hjelpetyper (Observatør, planleggere, fag) og operatører for å jobbe med arrangementer som med samlinger (kart, filtrer, reduser, hver og lignende fra JavaScript Array).

La oss forstå de grunnleggende konseptene til dette biblioteket.

Observerbar, observer, produsent

Observerbar er den første grunnleggende typen vi skal se på. Denne klassen inneholder hoveddelen av RxJs-implementeringen. Den er knyttet til en observerbar strøm, som kan abonneres på ved å bruke abonneringsmetoden.

Observable implementerer en hjelpemekanisme for å lage oppdateringer, den såkalte Observatør. Kilden til verdier for observatøren kalles Produsent. Dette kan være en array, iterator, web-socket, en slags hendelse osv. Så vi kan si at observable er en dirigent mellom produsent og observatør.

Observable håndterer tre typer Observer-hendelser:

  • neste – nye data
  • feil – en feil hvis sekvensen ble avsluttet på grunn av et unntak. denne hendelsen innebærer også fullføring av sekvensen.
  • komplett — signal om fullføringen av sekvensen. Det betyr at det ikke kommer flere nye data.

La oss se demoen:

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

I begynnelsen vil vi behandle verdiene 1, 2, 3 og etter 1 sekund. vi får 4 og avslutter strømmen vår.

Tenke høyt

Og så skjønte jeg at det var mer interessant å fortelle det enn å skrive om det. 😀

Abonnement

Når vi abonnerer på en strøm, oppretter vi en ny klasse abonnementsom gir oss muligheten til å avslutte abonnementet ved hjelp av metoden annulere abonnementet. Vi kan også gruppere abonnementer ved hjelp av metoden legge til. Vel, det er logisk at vi kan fjerne grupperingen av trådene ved hjelp av fjerne. Legg til og fjern metodene godtar et annet abonnement som input. Jeg vil merke meg at når vi melder av, melder vi av alle barneabonnementer som om de hadde kalt avmeldingsmetoden. Gå videre.

Typer bekker

HOT
KALD

Produsent er skapt utenfor observerbar
Produsent er opprettet inne observerbare

Data overføres på det tidspunktet det observerbare opprettes
Data oppgis på abonnementstidspunktet

Trenger ytterligere logikk for å melde deg av
Tråden avsluttes av seg selv

Bruker et en-til-mange forhold
Bruker et en-til-en forhold

Alle abonnementer har samme betydning
Abonnementer er uavhengige

Data kan gå tapt hvis du ikke har et abonnement
Reutsteder alle strømverdier for et nytt abonnement

For å gi en analogi, vil jeg tenke på en hot stream som en film på et teater. På hvilket tidspunkt du ankom, fra det øyeblikket begynte du å se. Jeg vil sammenligne en kald flyt med en samtale innen teknologi. Brukerstøtte. Alle som ringer lytter til talepostopptaket fra start til slutt, men du kan legge på ved å avslutte abonnementet.

Jeg vil merke meg at det også finnes såkalte varme strømmer (jeg har vært borti denne definisjonen ekstremt sjelden og kun i fremmede samfunn) - dette er en strøm som går over fra en kald strøm til en varm. Spørsmålet oppstår - hvor skal jeg bruke)) Jeg vil gi et eksempel fra praksis.

Jeg jobber med Angular. Han bruker aktivt rxjs. For å motta data til serveren forventer jeg en kald tråd og bruker denne tråden i malen ved å bruke asyncPipe. Hvis jeg bruker dette røret flere ganger, så går tilbake til definisjonen av en kald strøm, vil hvert rør be om data fra serveren, noe som er mildt sagt merkelig. Og hvis jeg konverterer en kald strøm til en varm, vil forespørselen skje én gang.

Generelt er det ganske vanskelig for nybegynnere å forstå typen flyt, men viktig.

Operatører

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

Operatører gir oss muligheten til å utvide vår evne til å jobbe med strømmer. De hjelper til med å kontrollere hendelsene som skjer i Observable. Vi skal se på et par av de mest populære, og flere detaljer om operatørene finner du ved å bruke lenkene i den nyttige informasjonen.

Operatører - av

La oss starte med hjelpeoperatøren til. Det skaper en observerbar basert på en enkel verdi.

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

Operatører - filter

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

Filteroperatøren, som navnet antyder, filtrerer strømsignalet. Hvis operatøren returnerer sann, hopper den videre.

Operatører - ta

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

take — Tar verdien av antall emittere, hvoretter tråden slutter.

Operatører - debounceTime

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

debounceTime - forkaster utsendte verdier som faller innenfor det angitte tidsintervallet mellom utganger - etter at tidsintervallet har passert, sender ut den siste verdien.

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

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

Operatører - takeWhile

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

Sender ut verdier til takeWhile returnerer false, hvoretter den avslutter abonnementet på tråden.

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

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

Operatører - kombinerSiste

combineLatest-operatøren ligner noe på løfte.all. Den kombinerer flere tråder til én. Etter at hver tråd gir minst én emisjon, får vi de siste verdiene fra hver i form av en matrise. Videre, etter ethvert utslipp fra de sammenslåtte bekkene, vil det gi nye verdier.

Asynkron programmering i 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));

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

Operatører - zip

Zip - Venter på en verdi fra hver tråd og danner en matrise basert på disse verdiene. Hvis verdien ikke kommer fra noen tråd, vil gruppen ikke bli dannet.

Asynkron programmering i 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));

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

Operatører - forkJoin

forkJoin kobler seg også til tråder, men den sender ut en verdi bare når alle tråder er fullført.

Asynkron programmering i 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);

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

Operatører - kart

Karttransformasjonsoperatøren forvandler emitterverdien til en ny.

Asynkron programmering i 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)
);

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

Operatører – del, trykk

Kranoperatoren lar deg gjøre bivirkninger, det vil si alle handlinger som ikke påvirker sekvensen.

Aksjeoperatøren kan gjøre en kald strøm til en varm.

Asynkron programmering i JavaScript (Callback, Promise, RxJs)

Vi er ferdige med operatørene. La oss gå videre til emnet.

Tenke høyt

Og så dro jeg for å drikke litt te. Jeg er lei av disse eksemplene 😀

Emnefamilie

Fagfamilien er et godt eksempel på varme strømmer. Disse klassene er en slags hybrid som fungerer samtidig som observerbare og observerende. Siden emnet er en varm tråd, er det nødvendig å melde seg av det. Hvis vi snakker om hovedmetodene, er disse:

  • neste – overføring av nye data til strømmen
  • feil – feil og trådavslutning
  • komplett – fullføring av tråden
  • abonner – abonner på en strøm
  • avregistrer – avmeld deg fra strømmen
  • asobservable – transformer til en observatør
  • toPromise – forvandles til et løfte

Det er 4 5 fagtyper.

Tenke høyt

Det var 4 personer som snakket på strømmen, men det viste seg at de la til en til. Som de sier, lev og lær.

Enkelt emne new Subject()– den enkleste typen fag. Laget uten parametere. Sender verdier mottatt kun etter abonnement.

Atferdsemne new BehaviorSubject( defaultData<T> ) – etter min mening den vanligste typen fag. Inngangen tar standardverdien. Lagrer alltid data fra siste utgave, som overføres ved abonnement. Denne klassen har også en nyttig verdimetode, som returnerer gjeldende verdi av strømmen.

ReplaySubject new ReplaySubject(bufferSize?: number, windowTime?: number) — Inndataene kan valgfritt ta som det første argumentet størrelsen på bufferen av verdier som den vil lagre i seg selv, og som det andre tiden vi trenger endringer.

AsyncSubject new AsyncSubject() — ingenting skjer når du abonnerer, og verdien returneres først når den er fullført. Bare den siste verdien av strømmen vil bli returnert.

WebSocketSubject new WebSocketSubject(urlConfigOrSource: string | WebSocketSubjectConfig<T> | Observable<T>, destination?: Observer<T>) — Dokumentasjonen er taus om ham, og dette er første gang jeg har sett ham. Hvis noen vet hva han gjør, vennligst skriv så legger vi det til.

Puh. Vel, vi har dekket alt jeg ønsket å fortelle deg i dag. Jeg håper denne informasjonen var nyttig. Du kan selv lese referanselisten i fanen nyttig informasjon.

nyttig informasjon

Kilde: www.habr.com

Legg til en kommentar