Asynchrone Programméierung am 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.

Asynchrone Programméierung am 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.

Asynchrone Programméierung am 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?

Asynchrone Programméierung am 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.

Loosst eis dat folgend Beispill kucken a fänken u mat senger step-by-step Ëmsetzung un. Loosst eis och kucken wat am System geschitt.

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

Asynchrone Programméierung am JavaScript. (Callback, Promise, RxJs)

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

Asynchrone Programméierung am JavaScript. (Callback, Promise, RxJs)

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

Asynchrone Programméierung am JavaScript. (Callback, Promise, RxJs)

3) An et ass erfëllt

Asynchrone Programméierung am JavaScript. (Callback, Promise, RxJs)

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

Asynchrone Programméierung am JavaScript. (Callback, Promise, RxJs)

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

Asynchrone Programméierung am 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.

Asynchrone Programméierung am JavaScript. (Callback, Promise, RxJs)

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

Asynchrone Programméierung am JavaScript. (Callback, Promise, RxJs)

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

Asynchrone Programméierung am JavaScript. (Callback, Promise, RxJs)

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

Asynchrone Programméierung am JavaScript. (Callback, Promise, RxJs)

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

Asynchrone Programméierung am 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.

Asynchrone Programméierung am JavaScript. (Callback, Promise, RxJs)

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

Asynchrone Programméierung am JavaScript. (Callback, Promise, RxJs)

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

Asynchrone Programméierung am JavaScript. (Callback, Promise, RxJs)

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

Asynchrone Programméierung am JavaScript. (Callback, Promise, RxJs)

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

Asynchrone Programméierung am JavaScript. (Callback, Promise, RxJs)

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

Loosst eis e Beispill an der Dynamik kucken:

Asynchrone Programméierung am 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.

Asynchrone Programméierung am 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 { interval1 } = Rx;
const { take } = RxOperators;

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

Asynchrone Programméierung am 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.

Asynchrone Programméierung am 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:

Asynchrone Programméierung am 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.

Asynchrone Programméierung am JavaScript. (Callback, Promise, RxJs)

Bedreiwer - Filter

Asynchrone Programméierung am 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

Asynchrone Programméierung am JavaScript. (Callback, Promise, RxJs)

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

Opérateuren - debounceTime

Asynchrone Programméierung am 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)
);  

Asynchrone Programméierung am JavaScript. (Callback, Promise, RxJs)

Opérateuren - huelenWhile

Asynchrone Programméierung am 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 )
);  

Asynchrone Programméierung am JavaScript. (Callback, Promise, RxJs)

Opérateuren - combineLatest

De kombinéierenLeschte Bedreiwer ass e bëssen ähnlech wéi verspriechen.all. Et kombinéiert verschidde thread an een. Nodeems all Fuedem op d'mannst eng Emissioun mécht, kréie mir déi lescht Wäerter vun all a Form vun enger Array. Weider, no all Emissioun vun de kombinéierte Stroum, gëtt et nei Wäerter.

Asynchrone Programméierung am 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));

Asynchrone Programméierung am 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.

Asynchrone Programméierung am 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));

Asynchrone Programméierung am 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.

Asynchrone Programméierung am 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);

Asynchrone Programméierung am JavaScript. (Callback, Promise, RxJs)

Opérateuren - Kaart

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

Asynchrone Programméierung am 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)
);

Asynchrone Programméierung am 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.

Asynchrone Programméierung am 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