In einem früheren Artikel haben wir uns angesehen, wie wir unsere eigenen RxJS-Anweisungen erstellen. Jetzt möchte ich über einen wenig bekannten Erstellungsoperator sprechen - defer
- und erklären, wie Sie ihn in bestimmten Situationen verwenden können
Angenommen, Sie müssen einen Operator erstellen, der eine Funktion übernimmt und sie nur einmal ausführt, wenn sie zum ersten Mal einen Wert erhält. Wir implementieren es unter dem Namen tapOnce
function tapOnce<T>(fn: Function): OperatorFunction<T, T> {
return function(source: Observable<any>) {
let run = false;
return source.pipe(
tap(() => {
if (!run) {
fn();
run = true;
}
})
);
};
}
Der Code ist klar - er wird tap
zum Ausführen der Funktion verwendet, das Flag wird run
dazu nur einmal benötigt. Jetzt benutzen wir den Operator.
const source = interval(5000).pipe(
tapOnce(() => console.log('+')
));
source.subscribe(console.log);
Alles funktioniert, das Pluszeichen wird erst beim ersten Senden in der Konsole angezeigt. Fügen Sie nun die Abonnenten hinzu.
const source = interval(5000).pipe(
tapOnce(() => console.log('+')
));
source.subscribe(console.log);
source.subscribe(console.log);
Wenn Sie sich die Konsole ansehen, gibt es nur ein Plus. Das Problem ist, dass beide Abonnenten dieselbe lexikalische Umgebung verwenden und auf dieselbe Variable verweisen run
. Wir brauchen eine Möglichkeit, die Erstellung eines Threads zu verschieben, bis sich jemand anmeldet.
Wird helfen defer
import { defer } from 'rxjs';
function tapOnce<T>(fn: Function): OperatorFunction<T, T> {
return function(source: Observable<any>) {
return defer(() => {
let run = false;
return source.pipe(
tap(() => {
if (!run) {
fn();
run = true;
}
})
);
});
};
}
Der Operator defer
akzeptiert eine Funktion, die zurückgegeben werden soll ObservableInput
. Der darin enthaltene Code defer
wird nur beim Abonnement und nicht während der Erstellung ausgeführt. Mit diesem Ansatz und dank js Schließung verwendet jeder Abonnent seine eigene lexikalische Umgebung.
Lassen Sie uns unsere einfache Implementierung defer
zum besseren Verständnis erstellen .
function defer(observableFactory: () => ObservableInput<any>) {
return new Observable(subscriber => {
const source = observableFactory();
return source.subscribe(subscriber);
});
}
defer
Gibt einen neuen Stream zurück, der zum Zeitpunkt des Abonnements von der Factory-Funktion erstellt wird und als Quelle verwendet wird.
Hier sind weitere Beispiele, wo es nützlich sein wird defer
. Angenommen, wir haben einen Ausdruck, der gezählt werden muss, wenn sich jemand anmeldet. zum Beispiel
const randNum = of(Math.random());
randNum.subscribe(console.log);
randNum.subscribe(console.log);
In diesem Beispiel erhält jeder Teilnehmer den gleichen Zufallswert. Sie können es so korrigieren, dass der Ausdruck beim Abonnieren gezählt wird, nicht beim Ansagen.
const randNum = defer(() => of(Math.random()));
randNum2.subscribe(console.log);
randNum2.subscribe(console.log);
const randNum = () => of(Math.random());
randNum2().subscribe(console.log);
randNum2().subscribe(console.log);
Ein weiteres Beispiel ist, wenn Sie die Ausführung eines Versprechens verzögern müssen.
const promise = new Promise(resolve => {
resolve();
});
const promiseDefered = defer(() => {
return new Promise(resolve => {
resolve();
});
});
promiseDefered.subscribe(console.log);
Versprechen werden sofort ausgeführt, unabhängig von der Anzahl der Zuhörer. Sie können ein Versprechen mit einem Stream (d defer
. H. Faul) aussehen lassen .