Rencontrez defer

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 taputilisé pour exécuter la fonction, l'indicateur n'est runné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 deferaccepte une fonction qui devrait retourner ObservableInput. Le code à l'intérieur defersera 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 deferpour 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);

// The same concept as using a function
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.


// This already executing regardless the numbers of handlers
const promise = new Promise(resolve => {
  resolve();
});

// Deferring the creation of the promise until someone subscribes
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.


All Articles