Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

Salute à tutti. In cuntattu Omelnitsky Sergey. Ùn tantu tempu, aghju ospitu un flussu nantu à a prugrammazione reattiva, induve aghju parlatu di asincronia in JavaScript. Oghje vogliu riassume stu materiale.

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

Ma prima di principià u materiale principale, avemu bisognu di fà una introduzione. Allora cuminciamu cù e definizione: chì sò stack and queue?

Pila hè una cullizzioni chì l'elementi sò recuperati nantu à una basa LIFO "last in, first out".

Gira hè una cullizzioni chì l'elementi sò ottenuti secondu u principiu ("first in, first out" FIFO

Va bè, cuntinuemu.

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

JavaScript hè un linguaghju di prugrammazione unicu filatu. Questu significa chì hà solu un filu di esecuzione è una pila induve e funzioni sò in fila per l'esekzione. Dunque, JavaScript pò esse realizatu solu una operazione à u tempu, mentre chì l'altri operazioni aspittàranu u so turnu nantu à a pila finu à ch'elli sò chjamati.

Call stack hè una struttura di dati chì, in termini simplici, registra infurmazioni nantu à u locu in u prugramma induve simu. Se saltamu in una funzione, spinghjemu a so entrata à a cima di a pila. Quandu vultemu da una funzione, popmu l'elementu più altu da a pila è finiscinu induve avemu chjamatu sta funzione. Hè tuttu ciò chì a pila pò fà. È avà una quistione assai interessante. Allora cumu funziona l'asincronia in JavasScript?

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

In fattu, in più di a pila, i navigatori anu una fila speciale per travaglià cù a chjamata WebAPI. E funzioni di sta fila saranu eseguite in ordine solu dopu chì a pila hè stata completamente sguassata. Solu dopu chì sò posti da a fila nantu à a pila per l'esecuzione. S'ellu ci hè almenu un elementu nantu à a pila in u mumentu, allora ùn ponu micca mette nantu à a pila. Solu per quessa, chjamà funzioni per timeout hè spessu imprecisa in u tempu, postu chì a funzione ùn pò micca passà da a fila à a pila mentre hè piena.

Fighjemu un ochju à l'esempiu seguente è andemu à traversu u passu à passu. Videmu ancu ciò chì succede in u sistema.

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

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

1) Finu à avà ùn succede nunda. A cunsola di u navigatore hè pulita, a pila di chjama hè viota.

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

2) Allora u cumandamentu console.log('Hi') hè aghjuntu à a pila di chjama.

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

3) È hè cumpletu

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

4) Allora console.log('Hi') hè eliminatu da a pila di chjama.

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

5) Avà andemu à u cumandimu setTimeout(function cb1() {… }). Hè aghjuntu à a pila di chjama.

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

6) U cumandimu setTimeout (funzione cb1 () {… }) hè eseguitu. U navigatore crea un timer chì face parte di l'API Web. Eseguirà un countdown.

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

7) U cumandamentu setTimeout (funzione cb1 () {… }) hà finitu u so travagliu è hè sguassatu da a pila di chjama.

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

8) U cumandamentu console.log('Bye') hè aghjuntu à a pila di chjama.

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

9) U cumandimu console.log('Bye') hè eseguitu.

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

10) U cumandamentu console.log('Bye') hè sguassatu da a pila di chjama.

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

11) Dopu chì almenu 5000 ms sò passati, u timer finisce è mette a callback cb1 in a fila di callback.

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

12) U loop di l'avvenimentu piglia a funzione cb1 da a fila di callback è u spinge nantu à a pila di chjama.

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

13) A funzione cb1 hè eseguita è aghjunghje console.log('cb1') à a pila di chjama.

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

14) U cumandimu console.log('cb1') hè eseguitu.

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

15) U cumandimu console.log('cb1') hè sguassatu da a pila di chjama.

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

16) A funzione cb1 hè eliminata da a pila di chjama.

Fighjemu un esempiu in dinamica:

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

Ebbè, avemu vistu cumu l'asincronia hè implementata in JavaScript. Avà parlemu brevemente di l'evoluzione di u codice asincronu.

L'evoluzione di u codice asincronu.

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

A prugrammazione asincrona cum'è a sapemu in JavaScript pò esse fatta solu cù e funzioni. Puderanu esse passati cum'è qualsiasi altra variabile à altre funzioni. Hè cusì chì sò nati i callbacks. È hè cool, divertente è fervente, finu à ch'ella si trasforma in tristezza, malincunia è tristezza. Perchè? Iè, hè simplice:

  • Cume a cumplessità di u codice cresce, u prughjettu si trasforma rapidamente in oscuri più blocchi nidificati - "callback hell".
  • A gestione di l'errore pò esse facilmente trascurata.
  • Ùn pudete micca rinvià espressioni cù ritornu.

Cù l'avventu di Promise, a situazione hè diventata un pocu megliu.

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

  • I catene di prumessa apparsu, chì migliurà a leghjibilità di u codice
  • Ci era un metudu separatu di interceptazione di l'errori
  • Esecuzione parallela cù Promise.all aghjuntu
  • Pudemu risolve l'asincronia nidificata cù async/wait

Ma a prumessa hà e so limitazioni. Per esempiu, una prumessa, senza ballà cù un tamburinu, ùn pò esse annullata, è più impurtante, travaglia cun un valore.

Ebbè, quì simu avvicinendu bè a prugrammazione reattiva. Stancu ? Ebbè, u bonu hè, pudete andà à fà un pocu di gabbiani, brainstorming è vultà à leghje più. È continueraghju.

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

Prugrammazione reattiva - un paradigma di prugrammazione focu annantu à i flussi di dati è a propagazione di cambiamenti. Fighjemu un ochju più vicinu à ciò chì hè un flussu di dati.

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

const eventsArray = [];

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

Imaginemu chì avemu un campu di input. Creemu un array, è per ogni keyup di l'avvenimentu di input, almacenaremu l'avvenimentu in u nostru array. À u listessu tempu, vogliu nutà chì u nostru array hè ordinatu per u tempu, i.e. l'indici di l'avvenimenti successivi hè più grande di l'indici di i primi. Un tali array hè un mudellu di flussu di dati simplificatu, ma ùn hè ancu un flussu. Per chì sta matrice sia chjamata in modu sicuru un flussu, deve esse capace d'infurmà à l'abbonati chì novi dati sò ghjunti in questu. Cusì venemu à a definizione di flussu.

Flussu di dati

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

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

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

Flussu hè una serie di dati ordinati per u tempu chì ponu indicà chì i dati sò cambiati. Avà imaginate quantu cunvene à scrive codice in quale avete bisognu di attivà parechji avvenimenti in diverse parti di u codice per una azione. Simplicemu abbonate à u flussu è ci dicerà quandu i cambiamenti accadenu. È a biblioteca RxJs pò fà questu.

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

RxJS hè una biblioteca per travaglià cù prugrammi asincroni è basati nantu à l'avvenimenti chì utilizanu sequenze osservabili. A biblioteca furnisce u tipu principale Osservabile, parechji tipi di helper (Osservatori, Schedulers, Sugetti) è operatori per travaglià cù avvenimenti cum'è cù cullezzione (mappa, filtru, riduce, ogni è simili da JavaScript Array).

Capemu i cuncetti basi di sta biblioteca.

Osservatore, Osservatore, Produttore

L'observable hè u primu tipu di basa chì guardemu. Questa classa cuntene a parte principale di l'implementazione RxJs. Hè assuciatu cù un flussu observable, chì pò esse abbonatu cù u metudu subscribe.

Observable implementa un mecanismu ausiliariu per creà aghjurnamenti, u cusì chjamatu Uttinutu. A fonte di i valori per un Osservatore hè chjamatu Producer. Pò esse un array, un iteratore, un socket web, un tipu d'avvenimentu, etc. Allora pudemu dì chì l'observable hè un cunduttore trà Producer è Observer.

Observable gestisce trè tippi di avvenimenti Observer:

  • prossimu - dati novi
  • errore - un errore se a sequenza terminata per una eccezzioni. questu avvenimentu implica ancu a fine di a sequenza.
  • cumpletu - un signalu nantu à a fine di a sequenza. Questu significa chì ùn ci saranu più dati novi

Videmu una demo:

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

À u principiu, processeremu i valori 1, 2, 3, è dopu à 1 sec. avemu 4 è finiscinu u nostru filu.

Pensendu à voce alta

È tandu aghju capitu chì era più interessante di dì chì di scrive. 😀

abbunamentu

Quandu avemu sottumessi à un flussu, creamu una nova classa abbunamentu, chì ci dà l'opzione di annullà cù u metudu unsubscribe. Pudemu ancu gruppu abbonamenti cù u metudu aghjunghje. Ebbè, hè logicu chì pudemu unaggregate i fili usendu smarisce. I metudi di aghjunghje è sguassate accettanu un abbunamentu diversu cum'è input. Vogliu nutà chì quandu avemu unsubscribe, avemu unsubscribe da tutti l'abbonamenti di i zitelli cum'è s'elli chjamanu ancu u metudu di unsubscribe. Avanti.

Tipi di flussi

HOT
FROIDU

U pruduttore hè creatu fora di l'observable
U pruduttore hè creatu in l'osservabile

I dati sò passati à u mumentu chì l'observable hè creatu
I dati sò furniti à u mumentu di l'abbonamentu.

Avete bisognu di più logica per annunzià l'abbonamentu
U filu finisce da sè stessu

Aduprà una relazione unu à parechji
Aduprà una relazione unu à unu

Tutti l'abbonamenti anu u listessu valore
L'abbonamenti sò indipendenti

I dati ponu esse persi s'ellu ùn ci hè micca abbunamentu
Riemette tutti i valori di flussu per un novu abbonamentu

Per dà una analogia, mi imaginassi un flussu caldu cum'è un filmu in un cinema. À chì puntu in u tempu hè vinutu, da quellu mumentu avete cuminciatu à fighjà. Puderia paragunà un flussu fretu cù una chjama in quelli. sustegnu. Qualchese chjamante ascolta a registrazione di a risponditrice da u principiu à a fine, ma pudete chjappà cun unsubscribe.

Vogliu nutà chì ci sò ancu chjamati flussi caldi (aghju scontru una tale definizione assai raramente è solu in e cumunità straniere) - questu hè un flussu chì si trasforma da un flussu friddu à un caldu. A quistione sorge - induve aduprà)) Daraghju un esempiu da a pratica.

Aghju travagliatu cù Angular. Utiliza attivamente rxjs. Per uttene dati à u servitore, aghju aspittatu un flussu friddu è aghju utilizatu stu flussu in u mudellu cù asyncPipe. Sè aduprate sta pipa parechje volte, allora, vultendu à a definizione di un flussu fretu, ogni pipa dumandarà dati da u servitore, chì hè stranu per dì u minimu. È se cunvertisce un flussu friddu à un caldu, allora a dumanda succederà una volta.

In generale, capisce u tipu di flussi hè abbastanza difficiule per i principianti, ma impurtante.

uperatori

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

L'operatori ci danu l'uppurtunità di travaglià cù i flussi. Aiutanu à cuntrullà l'avvenimenti chì scorri in u Observable. Avemu da cunsiderà un paru di i più populari, è più infurmazioni nantu à l'operatori ponu esse truvati in i ligami in l'infurmazioni utili.

Operatori-di

Cuminciamu cù l'operatore helper di. Crea un Observable basatu annantu à un valore simplice.

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

Operatori-filtru

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

L'operatore di filtru, cum'è u nome suggerisce, filtra u signale di u flussu. Se l'operatore torna vera, allora salta più.

Operatori - piglià

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

piglià - Piglia u valore di u numeru di emissioni, dopu chì u flussu finisci.

Operators-debounceTime

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

debounceTime - scarta i valori emessi chì cadenu in l'intervallu di tempu specificatu trà e dati di output - dopu chì l'intervallu di tempu hè passatu, emette l'ultimu valore.

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

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

Operators-takeWhile

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

Emette i valori finu à chì TakeWhile torna false, dopu annulla l'abbonamentu da u flussu.

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

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

Operatori-combineLatest

L'operatore cumminatu combineLatest hè un pocu simili à promise.all. Unisce parechji flussi in unu. Dopu chì ogni filu hà fattu almenu una emissione, avemu l'ultimi valori da ognunu cum'è un array. In più, dopu ogni emissione da i flussi cumminati, darà novi valori.

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

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

Operatori-zip

Zip - aspetta un valore da ogni flussu è forma un array basatu annantu à questi valori. Se u valore ùn vene micca da alcun filu, u gruppu ùn serà micca furmatu.

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

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

Operatori - forkJoin

forkJoin unisce ancu i fili, ma solu emette un valore quandu tutti i fili sò cumpleti.

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

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

Operatori-mappa

L'operatore di trasfurmazioni di mappa trasforma u valore di emissione in un novu.

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

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

Operatori - sparte, tocca

L'operatore di tap permette di fà effetti secundari, vale à dì qualsiasi azzioni chì ùn anu micca a sequenza.

L'operatore di utilità di sparte pò trasfurmà un flussu friddu in un caldu.

Programmazione asincrona in JavaScript (Callback, Promise, RxJs)

L'operatori sò fatti. Passemu à u Subject.

Pensendu à voce alta

E poi andò à beie tè. Sò stancu di sti esempi 😀

Famiglia di sughjettu

A famiglia di u sughjettu hè un primu esempiu di filamenti caldi. Queste classi sò un tipu di hibridu chì agisce cum'è observable è observatore à u stessu tempu. Siccomu u sughjettu hè un flussu caldu, deve esse unsubscribed da. Se parlemu di i metudi principali, allora questi sò:

  • dopu - passendu novi dati à u flussu
  • errore - errore è terminazione di filu
  • cumpletu - a fine di u filu
  • subscribe - subscribe to a stream
  • unsubscribe - unsubscribe da u flussu
  • asObservable - trasfurmà in un observatore
  • toPromise - si trasforma in una prumessa

Allocate 4 5 tipi di sugetti.

Pensendu à voce alta

Aghju dettu 4 nantu à u flussu, ma resultò chì anu aghjustatu unu più. Cume si dice, vive è amparà.

Sugettu simplice new Subject()- u tipu più simplice di sugetti. Creatu senza paràmetri. Passa i valori chì sò vinuti solu dopu l'abbonamentu.

CumportamentuSuggettu new BehaviorSubject( defaultData<T> ) - in my opinion u tipu più cumuna di sugetti-s. L'input piglia u valore predeterminatu. Salvà sempre i dati di l'ultima emissione, chì hè trasmessa quandu si subscribe. Questa classa hà ancu un metudu di valore utile chì torna u valore attuale di u flussu.

ReplaySubject new ReplaySubject(bufferSize?: number, windowTime?: number) - Opcionalmente, pò piglià cum'è u primu argumentu a dimensione di u buffer di i valori chì guarderà in sè stessu, è a seconda volta durante a quale avemu bisognu di cambiamenti.

asyncsubject new AsyncSubject() - nunda ùn succede quandu si sottoscrive, è u valore serà restituitu solu quandu hè cumpletu. Solu l'ultimu valore di u flussu serà tornatu.

WebSocketSubject new WebSocketSubject(urlConfigOrSource: string | WebSocketSubjectConfig<T> | Observable<T>, destination?: Observer<T>) - A ducumentazione hè in silenziu è eiu stessu a vecu per a prima volta. Quale sà ciò ch'ellu face, scrive, aghjunghjemu.

Uff. Ebbè, avemu cunsideratu tuttu ciò chì vulia dì oghje. Spergu chì sta infurmazione hè stata utile. Pudete leghje a lista di a literatura nantu à u vostru propiu in a tabulazione Informazioni utili.

nantu à u corsu Corsets

Source: www.habr.com

Add a comment