Bernamesaziya Asynchronous di JavaScript de. (Banga Vegere, Soz, RxJs)
Silav hemû. Sergey Omelnitsky di têkiliyê de ye. Demek berê min li ser bernamesaziya reaktîf mazûvaniya weşanek kir, li wir min di JavaScriptê de behsa asynkroniyê kir. Îro ez dixwazim li ser vê materyalê notan bikim.
Lê berî ku em dest bi materyalê sereke bikin, pêdivî ye ku em têbînîyek destpêkê bikin. Ji ber vê yekê em bi pênaseyan dest pê bikin: stok û dorek çi ye? Lod berhevokek e ku hêmanên wê li ser bingeha LIFO-ya paşîn, yekem-derve têne wergirtin Mar berhevokek e ku hêmanên wê li ser bingeha FIFO-ya yekem-derve, yekem-derve têne wergirtin
Baş e, em berdewam bikin.
JavaScript zimanek bernamesaziyê yek-têlek e. Ev tê wê wateyê ku bi tenê yek xêzek darvekirinê û yek stûnek heye ku fonksiyonên ji bo darvekirinê li ser têne rêz kirin. Ji ber vê yekê, JavaScript dikare di demekê de tenê yek operasyonê pêk bîne, dema ku operasyonên din dê li benda dora xwe li stikê bisekinin heya ku ew werin gazî kirin.
Call stack avahiyek daneyê ye ku, bi tenê, agahdariya li ser cîhê bernameya ku em lê ne tomar dike. Ger em derbasî fonksiyonek bibin, em têketina wê berbi jora stikê ve dikişînin. Dema ku em ji fonksiyonek vedigerin, em hêmana herî jorîn ji stikê derdixin û vedigerin cihê ku me jê re digot fonksiyonê. Ya ku stack dikare bike ev e. Û niha pirsek pir balkêş. Wê hingê asynkronî di JavasScriptê de çawa dixebite?
Di rastiyê de, ji bilî stackê, gerok ji bo xebata bi navê WebAPI re rêzek taybetî heye. Fonksiyonên di vê rêzê de tenê piştî ku stêk bi tevahî were paqij kirin dê bi rêzê bêne darve kirin. Tenê piştî vê yekê ji bo darvekirinê ew ji rêzê têne avêtin ser stikê. Heke di vê gavê de bi kêmanî yek hêmanek li ser stikê hebe, wê hingê ew nikarin li stikê werin zêdekirin. Tam ji ber vê yekê ye ku bangkirina fonksiyonan ji hêla demê ve bi gelemperî di wextê de ne rast e, ji ber ku fonksiyon dema ku ew tije ye nikare ji dorê bigire heya stikê.
Рассмотрим следующий пример и займёмся его пошаговым «выполнением». Также рассмотрим посмотрим, что при этом происходит в системе.
1) Hîn tiştek diqewime. Konsolê gerokê zelal e, stûna bangê vala ye.
2) Dûv re fermana konsolê.log('Hi') li stûna bangê tê zêdekirin.
3) Û pêk hat
4) Dûv re console.log('Silav') ji stoka bangê tê rakirin.
5) Naha biçin fermana setTimeout (fonksiyona cb1 () {… }). Ew li stoka bangê tê zêdekirin.
6) Fermana setTimeout (fonksiyona cb1() {… }) tê pêkanîn. Gerok demjimêrek ku beşek ji Web API-yê ye diafirîne. Ew ê jimartinê pêk bîne.
7) Fermana setTimeout(function cb1() {... }) karê xwe qedandiye û ji qata bangê tê derxistin.
8) Fermana konsol.log('Bye') li steka bangê tê zêdekirin.
9) Fermana konsol.log('Bye') tê bicihanîn.
10) Fermana konsolê.log('Bye') ji steka bangê tê derxistin.
11) Piştî ku bi kêmî ve 5000 ms derbas bûn, demjimêr diqede û vegerandina cb1 di rêza vegerê de cîh dike.
12) Kevirê bûyerê fonksiyona cb1 ji rêzika paşvekişandinê digire û li ser stûna bangê bi cih dike.
13) Fonksîyona cb1 tê bicihanîn û console.log('cb1') li staka bangê zêde dike.
14) Fermana konsolê.log('cb1') tê bicihkirin.
15) Fermana konsolê.log('cb1') ji stûna bangê tê derxistin.
16) Fonksîyona cb1 ji stûna bangê tê derxistin.
Ka em li mînakek dînamîk binêrin:
Welê, me nihêrî ka asynkronî çawa di JavaScriptê de tête bicîh kirin. Naha em bi kurtî li ser pêşveçûna koda asynchronous biaxivin.
Pêşveçûna koda asynchronous.
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);
})
})
})
})
})
});
Bernamesaziya asynkron wekî ku em di JavaScriptê de dizanin tenê ji hêla fonksiyonan ve dikare were bicîh kirin. Ew dikarin mîna her guhêrbarek din derbasî fonksiyonên din bibin. Bi vî rengî bangawaziyan çêbû. Û ew sar, kêf û lîstik e, heya ku vedigere xemgînî, melankolî û xemgîniyê. Çima? Ew hêsan e:
Her ku tevlîheviya kodê zêde dibe, proje zû vediguhere blokên nezelal, çend caran hêlîn - "dojehê vegera bangê".
Desthilatdariya çewtiyê dikare hêsan be ku meriv ji bîr neke.
Tu nikarî bêjeyan bi vegerê vegerînî.
Bi hatina Sozê re rewş hinekî baştir bû.
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);
});
Zencîreyên sozê xuya bûn, ku xwendina kodê çêtir kir
Rêbazek cihêreng ji bo girtina xeletiyan xuya bû
Ihtîmala darvekirina paralel bi karanîna Promise.all zêde kir
Em dikarin asynkroniya hêlînkirî bi karanîna async/bendê çareser bikin
Lê sozan kêmasiyên xwe hene. Mînakî, sozek bêyî reqsa bi tembûrê nayê betal kirin, û ya herî girîng ev e ku ew bi yek nirxê dixebite.
Welê, me bi rêkûpêk nêzîkê bernameya reaktîf kir. Westya? Welê, xwezî, hûn dikarin herin çayek çêkin, li ser bifikirin û vegerin da ku bêtir bixwînin. Û ez ê berdewam bikim.
Bernamekirina reaktîf paradîgmayeke bernamesaziyê ye ku li ser herikîna daneyan û belavkirina guherînê ye. Werin em ji nêz ve binihêrin ka herikîna daneyê çi ye.
// Получаем ссылку на элемент
const input = ducument.querySelector('input');
const eventsArray = [];
// Пушим каждое событие в массив eventsArray
input.addEventListener('keyup',
event => eventsArray.push(event)
);
Ka em bifikirin ku qada me ya têketinê heye. Em rêzek diafirînin û ji bo her keyupa bûyera têketinê em ê bûyerê di rêza xwe de hilînin. Di heman demê de, ez dixwazim bala xwe bidim ku rêzika me li gorî demê tê rêz kirin, ango. nîşaneya bûyerên paşerojê ji nîşaneya bûyerên berê mezintir e. Rêzeyek wusa modelek hêsankirî ya herikîna daneyê ye, lê ew hîn ne herikînek e. Ji bo ku ev array bi ewlehî wekî çemek were binav kirin, divê ew bi rengekî aboneyan agahdar bike ku daneyên nû tê de hatine. Bi vî awayî em tên ser pênaseya herikînê.
Herrikîn komek daneyan e ku li gorî demê hatine rêz kirin ku dikare destnîşan bike ku dane hatine guhertin. Naha bifikire ku meriv çawa kodê dinivîse ku tê de yek çalakiyek hewceyê bangkirina çend bûyeran li beşên cihê yên kodê dike. Em bi tenê dibin abonetiya streamê û gava ku guhertin çêbibin ew ê me agahdar bike. Û pirtûkxaneya RxJs dikare vê yekê bike.
RxJS pirtûkxaneyek e ji bo xebata bi bernameyên asynkron û-based bûyeran re bi karanîna rêzikên çavdêriyê. Pirtûkxane celebek bingehîn peyda dike Çavdêr, çend cureyên alîkar (Çavdêr, Bername, Mijar) û operatorên ji bo xebata bi bûyeran re wekî bi berhevokan (nexşe, parzûn, kêmkirin, her û yên mîna JavaScript Array).
Werin em têgehên bingehîn ên vê pirtûkxaneyê fam bikin.
Çavdêr, Çavdêr, Çêker
Observable yekem celebê bingehîn e ku em ê lê binêrin. Ev çîn beşa sereke ya pêkanîna RxJs dihewîne. Ew bi çemek çavdêrîkirî ve girêdayî ye, ku meriv dikare bi karanîna rêbaza abonetiyê were abone kirin.
Observable ji bo afirandina nûvekirinan mekanîzmayek arîkar pêk tîne, ku jê re tê gotin Çavdêr. Çavkaniya nirxan ji bo Çavdêr tê gotin Çêker. Ev dibe ku array, îterator, soketek malperê, celebek bûyer, hwd. Ji ber vê yekê em dikarin bibêjin ku observable di navbera Hilberîner û Çavdêr de rêgezek e.
Observable sê celeb bûyerên Observer digire:
paşê - daneyên nû
xelet - xeletiyek heke rêzik ji ber îstîsnayekê qediya. ev bûyer jî tê wateya temambûna rêzê.
temam - îşaretek li ser qedandina rêzê. Ev tê wê wateyê ku dê bêtir daneyên nû nebin.
Ka em demo bibînin:
Di destpêkê de em ê nirxên 1, 2, 3, û piştî 1 saniyeyê pêvajo bikin. em ê 4 bi dest bixin û herika xwe biqedînin.
Bi dengekî bilind difikire
Û paşê min fêm kir ku vegotina wê ji nivîsandina wê balkêştir bû. 😀
Abonetî
Dema ku em bibin abone li streamek em çînek nû diafirînin endamêku ji me re şiyana betalkirina bi karanîna rêbazê dide me betal bike. Her weha em dikarin bi karanîna rêbazê aboneyan kom bikin lêzêdekirin. Welê, mentiqî ye ku em dikarin têlan bikar bînin kom bikin dûrxistin. Rêbazên lê zêdekirin û jêbirinê abonetiya din wekî têketinê qebûl dikin. Ez dixwazim bibînim ku gava em betal dikin, em ji hemî aboneyên zarokan vediqetin mîna ku wan gazî rêbaza betalkirinê kiriye. Berdewam bike.
Cureyên çeman
GERM
SARMA
Hilberîner li derveyî çavdêriyê tê afirandin
Hilberîner di hundurê çavdêriyê de tê afirandin
Dane di dema ku çavdêrî tê afirandin de têne veguheztin
Daneyên di dema abonetiyê de têne peyda kirin
Ji bo betalkirinê mentiqek din lazim e
Mijar bi serê xwe bi dawî dibe
Têkiliya yek-bi-gelek bikar tîne
Têkiliya yek bi yek bikar tîne
Hemî abonetî heman wateyê ne
Abonetî serbixwe ne
Heke abonetiya we tune be, dane dikarin winda bibin
Ji bo abonetiyek nû hemî nirxên streamê ji nû ve diweşîne
Ji bo ku ez mînaheviyek bidim, ez ê wekî fîlimek di şanoyê de fîlimek germ bifikirim. Tu di kîjan demê de hatî, ji wê gavê ve te dest bi temaşekirinê kir. Ez ê herikîna sar bi bangek teknolojiyê re bidim ber hev. alîkarî. Her bangewazî ji serî heya dawiyê guhê xwe dide tomarkirina posta dengî, lê hûn dikarin bi karanîna nebin abonetiyê telefonê biqedînin.
Ez dixwazim bibînim ku bi navê herikîna germ jî hene (ez bi vê pênaseyê pir kêm û tenê di civatên biyanî de rast hatim) - ev herikînek e ku ji herikîna sar vediguhere ya germ. Pirs derdikeve holê - li ku tê bikar anîn)) Ez ê mînakek ji pratîkê bidim.
Ez bi Angular re dixebitim. Ew bi awayekî çalak rxjs bikar tîne. Ji bo wergirtina daneyan ji serverê re, ez li bendê me têlek sar û vê mijarê di şablonê de bi karanîna asyncPipe bikar bînin. Ger ez vê boriyê çend caran bikar bînim, wê hingê, vegerim ser pênase çemek sar, her boriyek dê daneyan ji serverê bixwaze, ku bi kêmanî xerîb e. Û heke ez kaniyek sar veguherînim germek germ, wê hingê daxwaz dê carekê çêbibe.
Bi gelemperî, têgihîştina celebê herikandinê ji bo destpêkek pir dijwar e, lê girîng e.
Operators
return this.http.get(`${environment.apiUrl}/${this.apiUrl}/trade_companies`)
.pipe(
tap(({ data }: TradeCompanyList) => this.companies$$.next(cloneDeep(data))),
map(({ data }: TradeCompanyList) => data)
);
Operator ji me re kapasîteya ku em karîna xwe ya xebata bi çeman re berfireh bikin peyda dikin. Ew alîkariya kontrolkirina bûyerên ku di Observable de diqewimin dikin. Em ê çend ji yên herî populer binihêrin, û bêtir agahdarî di derbarê operatoran de dikarin bi karanîna girêdanên di agahdariya kêrhatî de werin dîtin.
Operators - ji
Ka em bi operatorê alîkar ya dest pê bikin. Ew li ser bingeha nirxek hêsan, Çavdêriyek diafirîne.
Operators - Parzûna
Operatorê fîlterê, wekî ku ji navê xwe diyar dike, sînyala herikê fîlter dike. Ger operator rast vegere, ew bêtir dişoxilîne.
Operator - bigirin
bigire - Nirxa hejmara emitteran digire, piştî ku têl bi dawî dibe.
Operator - debounceTime
debounceTime - nirxên belavkirî yên ku di navbeyna dema diyarkirî ya di navbera derkan de dikevin berdide - piştî ku navberê derbas dibe, nirxa paşîn derdixe.
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)
);
Operator - takeWhile
Heya ku takeWhile derewîn vegerîne nirxan derdixe, piştî ku ew ji mijarê vediqete.
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 )
);
Operator - combinationLatest
Комбинированный оператор combineLatest чем-то похож на promise.all. Он объединяет несколько потоков в один. После того как каждый поток сделает хотя бы один эмит, мы получаем последние значения от каждого в виде массива. Далее, после любо любого эмита из объединённых потоков он будет отдавать новые значения.
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));
Operator - zip
Zip - Li benda nirxek ji her mijarê dimîne û li ser bingeha van nirxan rêzek çêdike. Heger nirx ji ti hêlekê dernekeve, wê demê kom çênabe.
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));
Operator - forkJoin
forkJoin jî tevlî mijaran dibe, lê ew tenê dema ku hemî mijar temam bibin nirxek derdixe.
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);
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)
);
Operator - parve bikin, tap bikin
Operatorê tap destûrê dide we ku hûn bandorên alî bikin, ango, her kiryarên ku bandorê li rêzê nakin.
Operatorê karûbarê parvekirinê dikare kaniyek sar veguherîne germek.
Em bi operatoran re qediyan. Ka em herin ser Mijar.
Bi dengekî bilind difikire
Û paşê ez çûm çayê vexwim. Ez ji van mînakan bêzar im 😀
Malbata mijarê
Malbata mijarê mînakek sereke ya herikîna germ e. Ev çîn celebek hîbrîdê ne ku di heman demê de wekî çavdêr û çavdêr tevdigerin. Ji ber ku mijar mijarek germ e, pêdivî ye ku meriv jê betal bibe. Ger em li ser rêbazên sereke biaxivin, wê hingê ev in:
paşê - veguheztina daneyên nû li ser çemê
xelet - xeletî û bidawîbûna mijarê
temam - temamkirina mijarê
bibin abone - bibin abone li herikekê
abonetiyê nekirin - ji herikînê veneqetin
asObservable - veguherîne çavdêrek
toPromise - vediguhere sozekê
4 5 cureyên mijaran hene.
Bi dengekî bilind difikire
Li ser çemê 4 kes dipeyivîn, lê derket holê ku wan yekî din lê zêde kiriye. Wekî ku dibêjin, bijîn û fêr bibin.
Mijara Hêsan new Subject()- cureya herî hêsan a mijaran. Bê pîvan hatine çêkirin. Nirxên ku tenê piştî abonetiyê hatine wergirtin vediguhezîne.
BehaviorSubject new BehaviorSubject( defaultData<T> ) - Bi dîtina min, mijara herî gelemperî ye. Input nirxa xwerû digire. Her gav daneyên jimareya paşîn, ya ku di dema abonetiyê de tê veguheztin, tomar dike. Ev çîn di heman demê de xwedan rêbazek nirxa kêrhatî ye, ku nirxa heyî ya tîrê vedigerîne.
ReplaySubject new ReplaySubject(bufferSize?: number, windowTime?: number) - Ketina vebijarkî dikare wekî argumana yekem mezinahiya tampona nirxan ku ew ê di xwe de hilîne, û wekî ya duyemîn dema ku tê de hewcedariya me bi guhertinan heye bigire.
AsyncSubject new AsyncSubject() - Di dema aboneyê de tiştek çênabe, û nirx tenê dema ku temam bibe dê were vegerandin. Tenê nirxa paşîn a herikê dê were vegerandin.
WebSocketSubject new WebSocketSubject(urlConfigOrSource: string | WebSocketSubjectConfig<T> | Observable<T>, destination?: Observer<T>) - Belge li ser wî bêdeng e û ez yekem car e ku wî dibînim. Ger kesek dizane ew çi dike, ji kerema xwe binivîse û em ê lê zêde bikin.
Phew. Welê, me her tiştê ku min dixwest îro ji we re bibêjim veşartiye. Ez hêvî dikim ku ev agahdarî kêrhatî bû. Hûn dikarin navnîşa referansan bixwe di tabloya agahdariya kêrhatî de bixwînin.