Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

Hi almal. In kontak Omelnitsky Sergey. Nie so lank gelede nie, het ek 'n stroom oor reaktiewe programmering aangebied, waar ek oor asynchronie in JavaScript gepraat het. Vandag wil ek hierdie materiaal opsom.

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

Maar voordat ons met die hoofmateriaal begin, moet ons 'n inleiding maak. So kom ons begin met definisies: wat is stapel en tou?

Stapel is 'n versameling waarvan die elemente op 'n "laaste in, eerste uit" LIFO-basis herwin word

draai is 'n versameling waarvan die elemente verkry word volgens die beginsel ("eerste in, eerste uit" EIEU

Goed, kom ons gaan voort.

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

JavaScript is 'n enkel-draad programmeertaal. Dit beteken dat dit slegs een draad van uitvoering en een stapel het waar funksies in die tou staan ​​vir uitvoering. Daarom kan JavaScript net een bewerking op 'n slag uitvoer, terwyl ander bewerkings hul beurt op die stapel sal wag totdat hulle geroep word.

Bel stapel is 'n datastruktuur wat, in eenvoudige terme, inligting opteken oor die plek in die program waar ons is. As ons in 'n funksie spring, druk ons ​​sy ingang na die bokant van die stapel. Wanneer ons van 'n funksie af terugkeer, spring ons die boonste element uit die stapel en eindig waar ons hierdie funksie vandaan geroep het. Dit is al wat die stapel kan doen. En nou 'n baie interessante vraag. Hoe werk asinchronie dan in JavasScript?

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

Trouens, bykomend tot die stapel, het blaaiers 'n spesiale tou om met die sogenaamde WebAPI te werk. Funksies uit hierdie tou sal eers in volgorde uitgevoer word nadat die stapel heeltemal skoongemaak is. Eers daarna word hulle uit die tou op die stapel geplaas vir uitvoering. As daar op die oomblik ten minste een element op die stapel is, kan hulle nie op die stapel kom nie. Net daarom is dit dikwels onakkuraat om funksies deur uitteltyd te bel, aangesien die funksie nie van die tou na die stapel kan kom terwyl dit vol is nie.

Kom ons kyk na die volgende voorbeeld en kom ons gaan stap vir stap daardeur. Kom ons kyk ook wat in die stelsel gebeur.

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

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

1) Tot dusver gebeur niks nie. Die blaaierkonsole is skoon, die oproepstapel is leeg.

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

2) Dan word die opdrag console.log('Hi') by die oproepstapel gevoeg.

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

3) En dit is vervul

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

4) Dan word console.log('Hi') uit die oproepstapel verwyder.

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

5) Kom ons gaan nou aan na die setTimeout(funksie cb1() {... }) opdrag. Dit word by die oproepstapel gevoeg.

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

6) Die setTimeout(funksie cb1() {... }) opdrag word uitgevoer. Die blaaier skep 'n timer wat deel is van die Web API. Dit sal 'n aftelling uitvoer.

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

7) Die setTimeout(funksie cb1() {... }) opdrag het sy werk voltooi en is verwyder uit die oproepstapel.

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

8) Die console.log('Bye') opdrag word by die oproepstapel gevoeg.

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

9) Die console.log('Bye') opdrag word uitgevoer.

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

10) Die opdrag console.log('Bye') word uit die oproepstapel verwyder.

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

11) Nadat ten minste 5000ms verloop het, eindig die timer en plaas die cb1-terugbel in die terugbel-tou.

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

12) Die gebeurtenislus neem funksie cb1 uit die terugbeltou en druk dit op die oproepstapel.

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

13) Die cb1-funksie word uitgevoer en voeg console.log('cb1') by die oproepstapel.

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

14) Die console.log('cb1') opdrag word uitgevoer.

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

15) Die opdrag console.log('cb1') word uit die oproepstapel verwyder.

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

16) Funksie cb1 word uit die oproepstapel verwyder.

Kom ons kyk na 'n voorbeeld in dinamika:

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

Wel, ons het gekyk hoe asinchronie in JavaScript geïmplementeer word. Kom ons praat nou kortliks oor die evolusie van asinchrone kode.

Die evolusie van asinchroniese 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);
                    })
                })
            })
        })
    })
});

Asinchroniese programmering soos ons dit in JavaScript ken, kan slegs met funksies gedoen word. Hulle kan soos enige ander veranderlike na ander funksies oorgedra word. Dit is hoe terugroepe gebore is. En dit is koel, pret en vurig, totdat dit verander in hartseer, weemoed en hartseer. Hoekom? Ja, dit is eenvoudig:

  • Soos die kompleksiteit van die kode groei, verander die projek vinnig in duistere veelvuldige geneste blokke - "terugbelhel".
  • Fouthantering kan maklik oor die hoof gesien word.
  • Jy kan nie uitdrukkings met terugkeer terugstuur nie.

Met die koms van Promise het die situasie 'n bietjie beter geword.

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

  • Beloftekettings het verskyn, wat die leesbaarheid van die kode verbeter het
  • Daar was 'n aparte metode van onderskepping van foute
  • Parallelle uitvoering met Promise.all bygevoeg
  • Ons kan geneste asinchronie oplos met asinc/await

Maar die belofte het sy beperkings. Byvoorbeeld, 'n belofte, sonder om met 'n tamboeryn te dans, kan nie gekanselleer word nie, en bowenal, dit werk met een waarde.

Wel, hier nader ons vlot reaktiewe programmering. Moeg? Wel, die goeie ding is, jy kan 'n paar meeue brou, dinkskrum en teruggaan om meer te lees. En ek sal voortgaan.

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

Reaktiewe programmering - 'n programmeringsparadigma gefokus op datavloei en die voortplanting van veranderinge. Kom ons kyk van naderby na wat 'n datastroom is.

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

const eventsArray = [];

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

Kom ons stel ons voor dat ons 'n invoerveld het. Ons skep 'n skikking, en vir elke insleutel van die invoergebeurtenis, sal ons die gebeurtenis in ons skikking stoor. Terselfdertyd wil ek daarop let dat ons skikking volgens tyd gesorteer word, m.a.w. die indeks van latere gebeurtenisse is groter as die indeks van vroeëre gebeurtenisse. So 'n skikking is 'n vereenvoudigde datavloeimodel, maar dit is nog nie 'n vloei nie. Om hierdie skikking veilig 'n stroom te noem, moet dit op een of ander manier intekenare kan inlig dat nuwe data daarin aangekom het. So kom ons by die definisie van vloei.

Data stroom

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

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

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

Vloei is 'n reeks data wat volgens tyd gesorteer is wat kan aandui dat die data verander het. Stel jou nou voor hoe gerieflik dit word om kode te skryf waarin jy verskeie gebeurtenisse in verskillende dele van die kode vir een aksie moet aktiveer. Ons teken eenvoudig op die stroom in en dit sal ons vertel wanneer veranderinge plaasvind. En die RxJs-biblioteek kan dit doen.

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

RxJS is 'n biblioteek om met asynchrone en gebeurtenisgebaseerde programme te werk deur waarneembare reekse te gebruik. Die biblioteek verskaf die hooftipe waarneembaar, verskeie helper tipes (Waarnemers, Skeduleerders, Vakke) en operateurs om met gebeurtenisse te werk soos met versamelings (kaart, filter, verminder, elke en soortgelyke van JavaScript Array).

Kom ons verstaan ​​die basiese konsepte van hierdie biblioteek.

Waarneembaar, waarnemer, vervaardiger

Waarneembaar is die eerste basistipe waarna ons sal kyk. Hierdie klas bevat die hoofgedeelte van die RxJs-implementering. Dit word geassosieer met 'n waarneembare stroom, waarop ingeteken kan word deur die intekenmetode te gebruik.

Observable implemente 'n hulpmeganisme vir die skep van opdaterings, die sogenaamde Observer. Die bron van waardes vir 'n waarnemer word genoem Producer. Dit kan 'n skikking, 'n iterator, 'n websok, 'n soort gebeurtenis, ens. Ons kan dus sê dat waarneembaar 'n geleier tussen Produsent en Waarnemer is.

Waarneembaar hanteer drie soorte Observer-gebeure:

  • volgende - nuwe data
  • fout - 'n fout as die volgorde beëindig is as gevolg van 'n uitsondering. hierdie gebeurtenis impliseer ook die einde van die ry.
  • voltooi - 'n sein oor die einde van die reeks. Dit beteken daar sal nie meer nuwe data wees nie

Kom ons kyk na 'n demo:

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

Aan die begin sal ons die waardes 1, 2, 3 verwerk en na 1 sek. ons kry 4 en eindig ons draad.

Hardop dink

En toe besef ek dis interessanter om te vertel as om daaroor te skryf. 😀

Teken In

Wanneer ons op 'n stroom inteken, skep ons 'n nuwe klas inskrywing, wat ons die opsie gee om met die metode uit te teken unsubscribe. Ons kan ook intekeninge groepeer deur die metode te gebruik voeg. Wel, dit is logies dat ons drade kan ontgroepeer met behulp van verwyder. Die byvoeg- en verwydermetodes aanvaar 'n ander intekening as invoer. Ek wil daarop let dat wanneer ons uitteken, ons uitteken van alle kinderintekeninge asof hulle ook die uittekenmetode genoem het. Gaan voort.

Soorte strome

wARM
KOUD

Produsent word buite die waarneembare geskep
Produsent is geskep binne waarneembaar

Data word deurgegee wanneer die waarneembare geskep word
Data word verskaf ten tyde van intekening.

Benodig meer logika om uit te teken
Draad eindig op sy eie

Gebruik 'n een-tot-baie-verhouding
Gebruik 'n een-tot-een verhouding

Alle intekeninge het dieselfde waarde
Intekeninge is onafhanklik

Data kan verlore gaan as daar geen intekening is nie
Heruitreik alle stroomwaardes vir 'n nuwe intekening

Om 'n analogie te gee, sou ek 'n warm stroom voorstel soos 'n fliek in 'n bioskoop. Op watter tydstip jy gekom het, van daardie oomblik af het jy begin kyk. Ek sal 'n koue stroom vergelyk met 'n oproep in daardie. ondersteun. Enige beller luister van begin tot einde na die antwoordmasjienopname, maar jy kan ophang met uitteken.

Ek wil graag daarop let dat daar ook sogenaamde warm strome is (ek het so 'n definisie uiters selde ontmoet en slegs in buitelandse gemeenskappe) - dit is 'n stroom wat van 'n koue stroom na 'n warm stroom verander. Die vraag ontstaan ​​- waar om te gebruik)) Ek sal 'n voorbeeld uit die praktyk gee.

Ek werk met Angular. Hy gebruik aktief rxjs. Om data na die bediener te kry, verwag ek 'n koue stroom en ek gebruik hierdie stroom in die sjabloon met asyncPipe. As ek hierdie pyp verskeie kere gebruik, dan, om terug te keer na die definisie van 'n koue stroom, sal elke pyp data van die bediener aanvra, wat om die minste te sê vreemd is. En as ek 'n koue stroom na 'n warm een ​​omskakel, dan sal die versoek een keer gebeur.

Oor die algemeen is dit nogal moeilik vir beginners om die tipe vloei te verstaan, maar dit is belangrik.

Operateurs

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

Operateurs bied ons die geleentheid om met strome te werk. Hulle help om die gebeure wat in die Waarneembare vloei te beheer. Ons sal 'n paar van die gewildste oorweeg, en meer inligting oor die operateurs kan gevind word by die skakels in nuttige inligting.

Operateurs-van

Kom ons begin met die helper operateur van. Dit skep 'n waarneembare gebaseer op 'n eenvoudige waarde.

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

Operateurs-filter

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

Die filteroperateur, soos die naam aandui, filtreer die stroomsein. As die operateur waar teruggee, slaan dit verder oor.

Operateurs - neem

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

neem - Neem die waarde van die aantal emissies, waarna die stroom eindig.

Operators-debounceTime

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

debounceTime - gooi uitgestraalde waardes wat binne die gespesifiseerde tydinterval tussen uitsetdata val, weg - nadat die tydinterval verby is, stuur die laaste waarde uit.

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

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

Operateurs-takeWhile

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

Stel waardes uit totdat takeWhile vals terugstuur, teken dan uit van die draad.

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

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

Operateurs-kombineer Nuutste

Die gekombineerde operateur combineLatest is ietwat soortgelyk aan below.all. Dit kombineer verskeie strome in een. Nadat elke draad ten minste een uitstraal gemaak het, kry ons die nuutste waardes van elkeen as 'n skikking. Verder, na enige vrystelling van die gekombineerde strome, sal dit nuwe waardes gee.

Asinchroniese programmering in 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));

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

Operateurs-zip

Zip - wag vir 'n waarde van elke stroom en vorm 'n skikking gebaseer op hierdie waardes. As die waarde nie van enige draad kom nie, sal die groep nie gevorm word nie.

Asinchroniese programmering in 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));

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

Operateurs - forkJoin

forkJoin sluit ook by drade aan, maar dit gee slegs 'n waarde uit wanneer alle drade voltooi is.

Asinchroniese programmering in 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);

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

Operateurs-kaart

Die kaarttransformasie-operateur verander die uitstraalwaarde in 'n nuwe een.

Asinchroniese programmering in 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)
);

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

Operateurs - deel, tik

Die kraanoperateur laat jou toe om newe-effekte te doen, dit wil sê enige aksies wat nie die volgorde beïnvloed nie.

Die aandeelnutsoperateur kan 'n koue stroom in 'n warm een ​​verander.

Asinchroniese programmering in JavaScript (Callback, Promise, RxJs)

Operateurs is klaar. Kom ons gaan aan na Onderwerp.

Hardop dink

En toe gaan drink ek tee. Ek is moeg vir hierdie voorbeelde 😀

Onderwerp familie

Die vakfamilie is 'n uitstekende voorbeeld van warm drade. Hierdie klasse is 'n soort baster wat terselfdertyd as waarneembaar en waarnemer optree. Aangesien die onderwerp 'n warm stroom is, moet dit uitgeteken word. As ons praat oor die hoofmetodes, dan is dit:

  • volgende - deurgee van nuwe data na die stroom
  • fout - fout en draadbeëindiging
  • voltooi - einde van die draad
  • teken in - teken in op 'n stroom
  • teken uit - teken uit van die draad
  • as waarneembaar - verander in 'n waarnemer
  • toPromise - verander in 'n belofte

Ken 4 5 tipes vakke toe.

Hardop dink

Ek het gesê 4 op die stroom, maar dit het geblyk dat hulle nog een bygevoeg het. Soos die spreekwoord sê, leef en leer.

Eenvoudige onderwerp new Subject()- die eenvoudigste soort vakke. Geskep sonder parameters. Slaag die waardes wat eers na die intekening gekom het, deur.

Gedrag Onderwerp new BehaviorSubject( defaultData<T> ) - myns insiens die mees algemene tipe vak-s. Die invoer neem die verstekwaarde. Stoor altyd die data van die laaste uitgawe, wat versend word wanneer u inteken. Hierdie klas het ook 'n bruikbare waarde metode wat die huidige waarde van die stroom terugstuur.

Herhaal Onderwerp new ReplaySubject(bufferSize?: number, windowTime?: number) - Opsioneel kan dit as die eerste argument die grootte van die buffer van waardes wat dit in sigself sal stoor neem, en die tweede keer waartydens ons veranderinge benodig.

asinconderwerp new AsyncSubject() - niks gebeur wanneer u inteken nie, en die waarde sal slegs teruggestuur word wanneer dit voltooi is. Slegs die laaste waarde van die stroom sal teruggestuur word.

WebSocketSubject new WebSocketSubject(urlConfigOrSource: string | WebSocketSubjectConfig<T> | Observable<T>, destination?: Observer<T>) - Die dokumentasie swyg daaroor en ek sien dit self vir die eerste keer. Wie weet wat hy doen, skryf, sal ons byvoeg.

Pff. Wel, ons het alles oorweeg wat ek vandag wou vertel. Hoop hierdie inligting was nuttig. Jy kan die lys literatuur op jou eie lees in die Nuttige Inligting-oortjie.

Nuttige inligting

Bron: will.com

Voeg 'n opmerking