Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

Moien alleguer. Sergey Omelnitsky ass a Kontakt. Net viru laanger Zäit hunn ech e Stream iwwer reaktiv Programméierung gehost, wou ech iwwer Asynchronie am JavaScript geschwat hunn. Haut wëll ech Notizen iwwer dëst Material maachen.

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

Awer ier mer d'Haaptmaterial ufänken, musse mir eng Aféierungsnotiz maachen. Also loosst eis mat Definitiounen ufänken: wat ass e Stack an eng Schlaang?

Stack ass eng Sammlung deenen hir Elementer op engem Last-In, First-Out LIFO Basis kritt ginn

Queue ass eng Sammlung deenen hir Elementer op enger First-In, First-Out FIFO Basis kritt ginn

Okay, loosst eis weidergoen.

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

JavaScript ass eng Single-threaded Programméierungssprooch. Dëst bedeit datt et nëmmen ee Fuedem vun der Ausféierung an engem Stack ass, op deem d'Funktioune fir d'Ausféierung gesat ginn. Dofir kann JavaScript nëmmen eng Operatioun gläichzäiteg ausféieren, während aner Operatiounen hiren Tour op de Stack waarden bis se opgeruff ginn.

Call Stack ass eng Datestruktur déi, einfach gesot, Informatioun iwwer d'Plaz am Programm registréiert wou mir sinn. Wa mir an eng Funktioun passéieren, drécke mir seng Entrée op d'Spëtzt vum Stack. Wa mir vun enger Funktioun zréckkommen, fuere mir dat iewescht Element vum Stack an enden zréck wou mir d'Funktioun genannt hunn. Dëst ass alles wat de Stack maache kann. An elo eng extrem interessant Fro. Wéi funktionéiert dann Asynchronie am JavasScript?

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

Tatsächlech, nieft dem Stack, hunn d'Browser eng speziell Schlaang fir mat der sougenannter WebAPI ze schaffen. D'Funktiounen an dëser Schlaang ginn nëmmen an Uerdnung ausgefouert nodeems de Stack komplett geläscht gouf. Eréischt duerno gi se aus der Schlaang op de Stack gedréckt fir d'Ausféierung. Wann et am Moment op d'mannst een Element um Stack ass, da kënnen se net op de Stack bäigefüügt ginn. Et ass genee dofir datt d'Funktioune vum Timeout ruffen dacks net präzis an der Zäit ass, well d'Funktioun net vun der Schlaang op de Stack kënnt wann se voll ass.

Рассмотрим следующий пример и займёмся его пошаговым «выполнением». Также посмотрим, что при этом происходит в системе.

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

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

1) Näischt geschitt nach. D'Browserkonsole ass kloer, den Uruffstack ass eidel.

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

2) Da gëtt de Kommando console.log('Hi') op den Uruffstack bäigefüügt.

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

3) An et ass erfëllt

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

4) Da gëtt console.log('Hi') aus dem Uruffstack geläscht.

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

5) Gitt elo op de Kommando setTimeout (Funktioun cb1 () {... }). Et gëtt op den Uruffstack bäigefüügt.

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

6) De Kommando setTimeout (Funktioun cb1 () {... }) gëtt ausgefouert. De Browser erstellt en Timer deen Deel vun der Web API ass. Et wäert e Countdown maachen.

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

7) De Kommando setTimeout (Funktioun cb1 () {... }) huet seng Aarbecht ofgeschloss a gëtt aus dem Uruffstack geläscht.

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

8) De Kommando console.log('Bye') gëtt op den Uruffstack bäigefüügt.

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

9) De Kommando console.log('Bye') gëtt ausgefouert.

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

10) De Kommando console.log ('Bye') gëtt aus dem Uruffstack geläscht.

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

11) Nodeem op d'mannst 5000 ms passéiert sinn, schléisst den Timer of a setzt den Callback cb1 an der Callback Queue.

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

12) D'Event Loop hëlt d'Funktioun cb1 aus der Callback Schlaang a setzt se op den Uruffstack.

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

13) Funktioun cb1 gëtt ausgefouert a füügt console.log ('cb1') un den Uruffstack.

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

14) De Kommando console.log('cb1') gëtt ausgefouert.

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

15) De Kommando console.log ('cb1') gëtt aus dem Uruffstack geläscht.

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

16) Funktioun cb1 gëtt aus dem Opruff Stack geläscht.

Loosst eis e Beispill an der Dynamik kucken:

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

Gutt, mir hu gekuckt wéi Asynchronie a JavaScript implementéiert ass. Loosst eis elo kuerz iwwer d'Evolutioun vum asynchrone Code schwätzen.

D'Evolutioun vum asynchrone Code.

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

Asynchron Programméierung wéi mir et am JavaScript kennen kann nëmme vu Funktiounen ëmgesat ginn. Si kënne wéi all aner Variabel op aner Funktiounen weidergeleet ginn. Dëst ass wéi Callbacks gebuer goufen. An et ass cool, lëschteg a spilleresch, bis et an Trauregkeet, Melancholie an Trauregkeet gëtt. Firwat? Et ass einfach:

  • Wéi d'Komplexitéit vum Code eropgeet, gëtt de Projet séier an obskur, ëmmer erëm nestéiert Blocks - "Callback Hell".
  • Fehlerhandhabung kann einfach sinn ze verpassen.
  • Dir kënnt net Ausdréck mat Retour zréck.

Mam Opkomme vu Promise gouf d'Situatioun e bësse besser.

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

  • Versprieche Ketten erschéngen, wat d'Code Liesbarkeet verbessert huet
  • Eng separat Method fir Feeler ze fangen ass erschéngt
  • D'Méiglechkeet vun der paralleler Ausféierung mat Promise.all bäigefüügt
  • Mir kënnen nested Asynchronie léisen mat Async / wait

Awer Verspriechen hunn hir Aschränkungen. Zum Beispill kann e Verspriechen net annuléiert ginn ouni mat enger Tambourin ze danzen, a wat wichteg ass datt et mat engem Wäert funktionnéiert.

Gutt, mir hu reaktiv Programméierung glat ukomm. Midd? Gutt, glécklecherweis kënnt Dir Téi maachen, iwwerdenken a kommen zréck fir méi ze liesen. An ech wäert weidergoen.

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

Reaktiv programméiere ass e Programméierungsparadigma fokusséiert op Datefloss a Verännerungsverbreedung. Loosst eis e méi no kucken wat en Datestroum ass.

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

const eventsArray = [];

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

Loosst eis virstellen datt mir en Inputfeld hunn. Mir kreéieren en Array, a fir all Input Event Keyup späichere mir den Event an eisem Array. Zur selwechter Zäit wéilt ech feststellen datt eis Array no Zäit zortéiert ass, d.h. den Index vu spéider Evenementer ass méi grouss wéi den Index vu fréiere. Sou eng Array ass e vereinfachte Modell vun engem Datefloss, awer et ass nach net e Floss. Fir datt dës Array sécher e Stream genannt gëtt, muss et fäeg sinn d'Abonnenten iergendwéi z'informéieren datt nei Daten dran ukomm sinn. Sou komme mir zu der Definitioun vu Flow.

Daten Stream

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

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

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

Flow ass eng Array vun Daten, déi no Zäit zortéiert sinn, déi kënnen uginn datt d'Donnéeën geännert hunn. Stellt Iech elo vir wéi bequem et gëtt Code ze schreiwen an deem eng Handlung e puer Eventer a verschiddene Sektioune vum Code erfuerdert. Mir abonnéieren einfach op de Stream an et wäert eis informéieren wann Ännerungen optrieden. An d'RxJs Bibliothéik kann dat maachen.

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

RxJS ass eng Bibliothéik fir mat asynchronen an event-baséierte Programmer ze schaffen mat beobachtbare Sequenzen. D'Bibliothéik bitt eng Basistyp Observéierbar, verschidde Hëllefstypen (Observateur, Scheduler, Sujeten) an Bedreiwer fir mat Eventer ze schaffen wéi mat Sammlungen (Kaart, Filter, reduzéieren, all an ähnlechen aus JavaScript Array).

Loosst eis d'Basiskonzepter vun dëser Bibliothéik verstoen.

Beobachtbar, Beobachter, Produzent

Observéierbar ass déi éischt Basistyp déi mir kucken. Dës Klass enthält den Haaptdeel vun der RxJs Ëmsetzung. Et ass mat engem beobachtbare Stroum assoziéiert, dee ka mat der Abonnementmethod abonnéiert ginn.

Observable implementéiert en Helfermechanismus fir Updates ze kreéieren, de sougenannte Observateur. D'Quell vu Wäerter fir den Observateur gëtt genannt Produzent. Dëst kéint eng Array, Iterator, Web Socket, eng Zort Event, etc. Also kënne mir soen datt beobachtbar en Dirigent tëscht Produzent an Observateur ass.

Observable handhabt dräi Aarte vun Observer Eventer:

  • nächst - nei Donnéeën
  • Feeler - e Feeler wann d'Sequenz eriwwer ass wéinst enger Ausnam. dëst Evenement implizéiert och d'Réalisatioun vun der Sequenz.
  • komplett - Signal iwwer d'Réalisatioun vun der Sequenz. Dëst bedeit datt et keng nei Donnéeë méi gëtt.

Loosst eis d'Demo kucken:

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

Am Ufank veraarbechte mir d'Wäerter 1, 2, 3, an no 1 Sekonn. mir kréien 4 an schléissen eise Stream of.

Haart denken

An dunn hunn ech gemierkt datt et méi interessant ass ze soen wéi doriwwer ze schreiwen. 😀

Abonnement

Wa mir op e Stream abonnéieren, kreéiere mir eng nei Klass Abonnementwat eis d'Méiglechkeet gëtt mat der Method ofzeschreiwen weggeet. Mir kënnen och Abonnementer gruppéiere mat der Method Foto. Gutt, et ass logesch datt mir d'Threads benotze kënnen ofgruppéieren ewechhuelen. D'Add- an Remove Methoden akzeptéieren en anert Abonnement als Input. Ech géif gären notéieren datt wa mir eis ofmelden, mir abonnéieren all Kand Abonnementer wéi wa se d'Abonnementmethod genannt hätten. Maach weider.

Zorte vu Baachen

Floumaart
KËLL

Produzent gëtt ausserhalb observéierbar erstallt
Produzent ass bannent beobachtbar erstallt

D'Date ginn iwwerdroen zur Zäit wou d'observéierbar erstallt gëtt
D'Donnéeë ginn zur Zäit vum Abonnement geliwwert

Braucht zousätzlech Logik fir sech ofzeschreiwen
De Fuedem endet op sech selwer

Benotzt eng eent-zu-vill Relatioun
Benotzt eng een-zu-een Relatioun

All Abonnementer hunn déi selwecht Bedeitung
Abonnementer sinn onofhängeg

Daten kënne verluer goen wann Dir keen Abonnement hutt
Gitt all Streamwäerter nei fir en neien Abonnement

Fir eng Analogie ze ginn, géif ech un e waarme Stream als Film an engem Theater denken. A wéi engem Zäitpunkt sidd Dir ukomm, vun deem Moment un hutt Dir ugefaang ze kucken. Ech géif e kal Flux zu engem Opruff am Tech vergläichen. ënnerstëtzen. All Uruffer lauschtert op d'Voicemail Opnam vun Ufank bis Enn, awer Dir kënnt ophänken andeems Dir Iech ofschreift.

Ech wëll bemierken datt et och sougenannte waarme Floss sinn (ech hunn dës Definitioun extrem selten an nëmmen an auslännesche Communautéiten begéint) - dëst ass e Floss, dee vun engem kale Floss op e waarme verwandelt. D'Fro stellt sech - wou ze benotzen)) Ech ginn e Beispill aus der Praxis.

Ech schaffen mat Angular. Hie benotzt aktiv rxjs. Fir Daten op de Server ze kréien, erwaarden ech e kale Fuedem a benotzen dëse Fuedem an der Schabloun mat asyncPipe. Wann ech dës Päif e puer Mol benotzen, dann, zréck op d'Definitioun vun engem kale Stroum, wäert all Päif Daten vum Server ufroen, wat komesch ass am mannsten ze soen. A wann ech e kale Stroum an e waarme konvertéieren, da geschitt d'Ufro eemol.

Am Allgemengen, Versteesdemech der Zort vun Flux ass relativ schwéier fir Ufänger, mä wichteg.

Operatoren

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

D'Operateure bidden eis d'Fäegkeet fir eis Fäegkeet auszebauen fir mat Streamen ze schaffen. Si hëllefen d'Evenementer ze kontrolléieren déi am Observable optrieden. Mir kucken op e puer vun de beléifsten, a méi Detailer iwwert d'Opérateuren kann mat de Linken an der nëtzlech Informatiounen fonnt ginn.

Opérateuren - vun

Loosst d'mat der Hëllef Bedreiwer ufänken vun. Et erstellt en Observable baséiert op engem einfache Wäert.

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

Bedreiwer - Filter

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

De Filteroperateur, wéi den Numm et scho seet, filtert de Stroumsignal. Wann de Bedreiwer richteg zréckkënnt, spréngt et weider.

Opérateuren - huelen

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

huelen - Huelt de Wäert vun der Unzuel vun den Emittenten, no deem de Fuedem endet.

Opérateuren - debounceTime

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

debounceTime - verwerft emittéiert Wäerter déi am spezifizéierten Zäitintervall tëscht Ausgänge falen - nodeems d'Zäitintervall passéiert ass, emitt de leschte Wäert.

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

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

Opérateuren - huelenWhile

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

Emittéiert Wäerter bis takeWhile falsch zréckkënnt, duerno ofschreift se vum Fuedem.

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

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

Opérateuren - combineLatest

Комбинированный оператор combineLatest чем-то похож на promise.all. Он объединяет несколько потоков в один. После того как каждый поток сделает хотя бы один эмит, мы получаем последние значения от каждого в виде массива. Далее, после любого эмита из объединённых потоков он будет отдавать новые значения.

Asynchronous Programméierung a 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));

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

Bedreiwer - zip

Zip - Waart op e Wäert vun all Fuedem a bildt eng Array baséiert op dëse Wäerter. Wann de Wäert net aus engem Fuedem kënnt, da gëtt d'Grupp net geformt.

Asynchronous Programméierung a 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));

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

Opérateuren - forkJoin

forkJoin trëtt och op Threads, awer et emittéiert nëmmen e Wäert wann all Threads komplett sinn.

Asynchronous Programméierung a 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);

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

Opérateuren - Kaart

De Kaarttransformatiounsbedreiwer transforméiert den Emitterwäert an en neien.

Asynchronous Programméierung a 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)
);

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

Bedreiwer - deelen, tippen

Den Tapoperateur erlaabt Iech Nebenwirkungen ze maachen, dat heescht all Aktiounen déi d'Sequenz net beaflossen.

Den Share Utility Bedreiwer kann e kale Stroum an e waarme maachen.

Asynchronous Programméierung a JavaScript (Callback, Promise, RxJs)

Mir si fäerdeg mat de Bedreiwer. Loosst eis op d'Thema weidergoen.

Haart denken

An dunn sinn ech e bëssen Téi drénken. Ech sinn midd vun dëse Beispiller 😀

Sujet Famill

D'Thema Famill ass e prime Beispill vu waarme Flëss. Dës Klassen sinn eng Zort Hybrid déi gläichzäiteg als beobachtbar an als Beobachter handelen. Well d'Thema e waarme Fuedem ass, ass et néideg dervun ofzeschreiwen. Wa mir iwwer d'Haaptmethoden schwätzen, da sinn dës:

  • nächst - Transfert vun neien Donnéeën op de Baach
  • Feeler - Feeler a Fuedem Enn
  • komplett - Ofschloss vum Fuedem
  • abonnéieren - abonnéieren op eng Baach
  • unsubscribe - ofzeschreiwen aus dem Stream
  • asObservable - transforméiert an en Beobachter
  • toPromise - verwandelt sech an e Verspriechen

Et gi 4 5 Zorte vu Sujeten.

Haart denken

Et waren 4 Leit déi um Stream geschwat hunn, awer et huet sech erausgestallt datt se nach eng bäigefüügt hunn. Wéi se soen, liewen a léieren.

Einfach Sujet new Subject()- déi einfachst Aart vu Sujeten. Erstellt ouni Parameteren. Iwwerdréit Wäerter déi nëmmen nom Abonnement kritt goufen.

BehuelenSubject new BehaviorSubject( defaultData<T> ) - menger Meenung no, déi heefegst Aart vu Sujet. Den Input hëlt de Standardwäert. Spuert ëmmer d'Donnéeë vun der leschter Emissioun, déi beim Abonnement iwwerdroen ginn. Dës Klass huet och eng nëtzlech Wäertmethod, déi den aktuelle Wäert vum Stroum zréckkënnt.

ReplaySubject new ReplaySubject(bufferSize?: number, windowTime?: number) - Den Input kann optional als éischt Argument d'Gréisst vum Puffer vu Wäerter huelen, déi et a sech selwer späichert, an als zweet d'Zäit wou mir Ännerungen brauchen.

AsyncSubject new AsyncSubject() - näischt geschitt wann Dir abonnéiert, an de Wäert gëtt nëmmen zréckginn wann se fäerdeg sinn. Nëmmen de leschte Wäert vum Stream gëtt zréck.

WebSocketSubject new WebSocketSubject(urlConfigOrSource: string | WebSocketSubjectConfig<T> | Observable<T>, destination?: Observer<T>) - D'Dokumentatioun ass roueg iwwer hien an ech gesinn hien fir d'éischte Kéier. Wann iergendeen weess wat hien mécht, schreift w.e.g. a mir addéieren et.

Pff. Gutt, mir hunn alles ofgedeckt wat ech Iech haut wollt soen. Ech hoffen dës Informatioun war nëtzlech. Dir kënnt d'Lëscht vun de Referenzen selwer an der nëtzlech Informatiounstab liesen.

hëllefräich Informatiounen

Source: will.com

Setzt e Commentaire