Dans un article précédent , nous avons examiné comment créer nos propres instructions RxJS. Maintenant, je veux parler d'un opérateur de création peu connu - defer
- et expliquer comment vous pouvez l'utiliser dans certaines situations
Supposons que vous deviez créer un opérateur qui prend une fonction et l'exécute une seule fois, la première fois qu'il reçoit une valeur. Nous l'implémentons sous le nom 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;
}
})
);
};
}
Le code est clair - il est tap
utilisé pour exécuter la fonction, l'indicateur n'est run
nécessaire pour le faire qu'une seule fois. Maintenant, nous utilisons l'opérateur.
const source = interval(5000).pipe(
tapOnce(() => console.log('+')
));
source.subscribe(console.log);
Tout fonctionne, le signe plus ne s'affiche dans la console qu'au premier émetteur. Ajoutez maintenant les abonnés.
const source = interval(5000).pipe(
tapOnce(() => console.log('+')
));
source.subscribe(console.log);
source.subscribe(console.log);
Si vous regardez la console - il n'y a qu'un seul plus. Le problème est que les deux abonnés utilisent le même environnement lexical et se réfèrent à la même variable run
. Nous avons besoin d'un moyen de reporter la création d'un fil jusqu'à ce que quelqu'un s'abonne.
Aidera 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;
}
})
);
});
};
}
L'opérateur defer
accepte une fonction qui devrait retourner ObservableInput
. Le code à l'intérieur defer
sera exécuté uniquement lors de l'inscription, et non lors de la création. En utilisant cette approche et grâce à la fermeture js, chaque abonné utilise son propre environnement lexical.
Créons notre implémentation simple defer
pour une meilleure compréhension.
function defer(observableFactory: () => ObservableInput<any>) {
return new Observable(subscriber => {
const source = observableFactory();
return source.subscribe(subscriber);
});
}
defer
renvoie un nouveau flux, qui est créé au moment de l'abonnement par la fonction d'usine, et sera utilisé comme source.
Voici d'autres exemples où cela sera utile defer
. Disons que nous avons une expression qui doit être comptée lorsque quelqu'un s'inscrit. par exemple
const randNum = of(Math.random());
randNum.subscribe(console.log);
randNum.subscribe(console.log);
Dans cet exemple, chaque abonné recevra la même valeur aléatoire. Vous pouvez le corriger pour que l'expression soit comptée lors de l'inscription, pas lors de l'annonce.
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);
Un autre exemple est lorsque vous devez retarder l'exécution d'une promesse.
const promise = new Promise(resolve => {
resolve();
});
const promiseDefered = defer(() => {
return new Promise(resolve => {
resolve();
});
});
promiseDefered.subscribe(console.log);
Les promesses sont exécutées immédiatement, quel que soit le nombre d'auditeurs. Vous pouvez faire une promesse ressembler à un flux (c'est-à-dire paresseux) en utilisant defer
.