Fermer

janvier 5, 2022

Introduction aux observables (RxJS)—Partie 1


Dans le premier article en deux parties, nous couvrirons les bases des observables, des observateurs et de RxJS.

Les observables offrent un moyen unifié de travailler avec différents types de données. C'est-à-dire que les observables peuvent émettre une valeur unique ou une séquence de valeurs, de manière synchrone ou asynchrone, paresseusement (froid) ou avidement (chaud), monodiffusion à un seul consommateur (froid) ou multidiffusion à plusieurs consommateurs (chaud).

 Chaton jouant avec une plante. Représenter les observables.

Crédit photo : Dim Hou sur Unsplash

Dans cette série d'articles en deux parties, nous allons examiner le type observable, apprendre à créer une instance observable et devenir familiarisé avec la fonction d'abonnement. Nous verrons que les observables sont des producteurs de données et les observateurs sont les consommateurs – s'abonner et se désabonner des observables – ainsi qu'expliquer des terminologies telles que « émettre une séquence de valeurs ».

Commençons par le début ! 🐣

Qu'est-ce qu'un observable ?

« Un observable représente une séquence de valeurs qui peuvent être observées ». —TC39

Contrairement aux promesses et aux protocoles d'itérationles observables ne font pas encore partie de JavaScript. Cependant, il existe une proposition TC39 pour ajouter un type observable à JavaScript.

Découvrez ce qu'est un observable et ce qu'il fait en étudiant la proposition TC39.

Un observable est un type.

La proposition TC39 introduit le type observable comme suit :

  • Le type observable peut être utilisé pour modéliser des sources de données push telles que des événements DOM, des intervalles de temps et des sockets.
  • Le Observable le constructeur initialise un nouvel objet observable.
const myObservable$ = new Observable(subscriber);

function[19659021]abonné(observateur) {
  
  
  retour () => {
	
  };
 }
  • L'argument abonné doit être un objet fonction. Elle est appelée à chaque fois que la méthode subscribe() de l'objet observable est invoquée.

Pour créer une instance observable, nous implémentons l'observable dans une fonction et passons la fonction au constructeur observable. La proposition TC39 fait référence à cette fonction en tant que fonction d'abonné. La fonction d'abonné sera invoquée à chaque fois que nous nous abonnerons à l'instance observable. Quelle doit être l'entrée et que doit-elle retourner ?

La proposition TC39 mentionne que le type observable peut être utilisé pour modéliser des sources de données push.

Un observable produit des données et les envoie à l'observateur

I ont écrit un article séparé "Comparing Data Producers in JavaScript" qui parle des producteurs de données et des systèmes de données push vs pull.

Comme expliqué dans l'article d'accompagnement, notre application inclut du code qui produit des données (producteurs ) et du code qui consomme des données (consommateurs).

Les fonctions, les promesses, les itérables et les observables sont les producteurs de données en JavaScript. C'est pourquoi la proposition du TC39 dit que le type observable peut être utilisé pour modéliser une source de données. « Basé sur le push » signifie que les observables contrôlent le moment où ils envoient des données à leurs observateurs.

Les producteurs diffèrent dans la façon dont ils communiquent les données avec leurs consommateurs. C'est-à-dire qu'ils peuvent avoir un système push ou pull, produire une valeur unique ou une séquence de valeurs, envoyer des données de manière synchrone ou asynchrone, paresseusement ou avidement.

Le point clé est qu'un observable produit des données et envoie les données à son consommateurs. Les données produites par un observable sont consommées par ses observateurs (ou abonnés).

Puisque nous définissons ce que fait une instance observable dans sa fonction d'abonné, la fonction d'abonné prend un observateur en entrée, produit des données, envoie les données à l'observateur , et informe l'observateur si une erreur s'est produite ou s'il a terminé d'envoyer des données. observable.

L'observable doit savoir à qui envoyer les données. Nous informons un observable qu'un observateur est intéressé à recevoir des données en s'y abonnant.

Le type observable a une méthode subscribe() qui accepte un observateur comme paramètre.

const souscription = myObservable$.subscribe(observer);

La méthode subscribe() commence envoyer des valeurs à l'objet observateur fourni en exécutant la fonction d'abonné de l'objet observable. une dispute. La fonction d'abonné commence alors à produire des données et à émettre des valeurs (ou des notifications) en exécutant les rappels de l'observateur.

Un Observable permet à ses observateurs de se désabonner

La méthode subscribe() renvoie un abonnement objet qui peut être utilisé pour annuler l'abonnement.

const subscription = myObservable$.subscribe(observer) ;

L'objet d'abonnement a une méthode appelée unsubscribe() qui permet à l'observateur de se désabonner (ou d'annuler l'abonnement) :

const subscription.[19659049]unsubscribe();

L'appel à unsubscribe() efface les ressources utilisées par l'abonnement et appelle la fonction de démontage renvoyée par la fonction d'abonné.

function[19659021]abonné(observateur)[19659023]{
  
  
  
  return () => {
    
  };
}

Qu'est-ce qu'un observateur ?

Un observateur est le consommateur des données produites par l'observable. Il est représenté par un objet avec les propriétés nexterror et complete. Ces propriétés contiennent des fonctions de rappel pour le traitement des données, la gestion des erreurs et des notifications d'achèvement.

La fonction d'abonné émet des données vers l'observateur en appelant la fonction de rappel next(). De même, il peut envoyer une notification d'erreur en appelant le rappel error() et une notification d'achèvement en appelant le rappel complete().

function subscriber (observateur) {
  observateur.suivant('Bonjour !');
  observer.complete();
}

Qu'est-ce que RxJS?

Comme nous l'avons mentionné précédemment, le type observable ne fait pas encore partie de JavaScript. Cependant, nous pouvons utiliser des bibliothèques qui implémentent le type observable.

Les implémentations de l'observable incluent :

Nous pouvons voir à partir des téléchargements hebdomadaires de npm que RxJS est extrêmement populaire. 🔥

RxJS signifie Reactive Extensions pour JavaScript. Selon la documentation :

RxJS est une bibliothèque pour composer des programmes asynchrones et basés sur des événements en utilisant des séquences observables.

La bibliothèque RxJS implémente :

  • Le type observable.
  • Le types associés : observateur, programmateur et sujet.
  • Un ensemble de fonctions de création observables. Les fonctions de création d'observables facilitent la création d'observables à partir de sources de données courantes—par exemple, interval()fromEvent() et range()—ainsi que combiner des observables—par exemple, concat()race() et zip().
  • Un ensemble d'opérateurs. Les opérateurs nous laissent opérer sur chaque élément de la séquence de données observables. Les opérateurs RxJS couvrent de nombreuses opérations que nous pourrions souhaiter effectuer sur nos données. Celles-ci incluent des opérations pour transformer des données, filtrer des données, effectuer des calculs mathématiques et plus encore. map()filter() et reduce() sont des exemples d'opérateurs fournis par RxJS que nous connaissons déjà à partir de tableaux en JavaScript.[19659033]Dans cet article, nous nous concentrerons sur les types observable et observateur.

    Regardons ensuite de plus près le type observable dans RxJS. 🔎

    La classe Observable dans RxJS

    RxJS implémente observable en tant que classe avec un constructeur, des propriétés et des méthodes.

    Les méthodes les plus importantes dans la classe observable sont subscribe et pipe :

    • subscribe() nous permet de souscrire à une instance observable.
    • pipe() nous permet appliquer une chaîne d'opérateurs à l'observable avant d'y souscrire. (Si vous êtes intéressé, vous pouvez lire Une explication simple du tube fonctionnel en JavaScript par Ben Lesh pour apprendre comment la fonction pipe permet l'arborescence, ce qui n'est pas possible avec l'augmentation de prototype.)

    La classe observable a également la méthode suivante :

    • forEach()—un moyen non annulable de s'abonner à un observable, à utiliser avec des API qui attendent des promesses

    En outre, la classe observable a plusieurs propriétés protégées pour l'usage interne de la bibliothèque RxJS, ce qui signifie que nous ne devons pas utiliser ces propriétés directement dans notre code d'application.

    Création d'un Observable dans RxJS

    Comme prévu, nous utilisons le constructeur observable pour créer une instance d'observable :[19659048]import { Observable } from 'rxjs';

    const myObservable$ = nouveau Observable[19659018](abonné);

    fun ction abonné(observateur) {

    retour () => {

    };
    }

    Créer un observable dans RxJS est à peu près la même chose que ce que nous avons vu dans la proposition TC39, sauf que nous devons importer la classe observable de la bibliothèque RxJS pour l'utiliser.

    Il est d'usage d'ajouter le signe $ à la fin du nom de la variable contenant une observable. Il s'agit d'une convention utile lancée par Andre Stalz qui permet de voir facilement en un coup d'œil que nous travaillons avec un observable.

    Si nous inspectons l'instance observable ci-dessus, nous voyons qu'il a le Subscribe() et pipe()ainsi que forEach() et les propriétés privées.

    Les méthodes suivantes de la liste ont été dépréciées et seront supprimées dans RxJS v8 :

    • toPromise()—renvoie une promesse qui se résout à la dernière valeur émise par l'observable lorsqu'elle se termine. Il a été remplacé par firstValueFrom et lastValueFrom et sera supprimé dans la v8. Veuillez vous référer à https://rxjs.dev/deprecations/to-promise et à cet article inDepthDev—RxJS heads up : toPromise is being deprecated—pour plus de détails.
    • lift()—crée une nouvelle observable, avec cette instance observable comme source et l'opérateur passé défini comme l'opérateur de la nouvelle observable. Cependant, il s'agit d'un détail d'implémentation et nous ne devons pas l'utiliser directement dans notre code d'application. Elle sera rendue interne dans la v8.

    liste d'instances observables : _isScalar, _subscribe, _trySubscribe, forEach, lift, operator, pipe, source, subscription, toPromise

    The Subscribe Function

    The Le constructeur observable attend une fonction comme paramètre. La bibliothèque RxJS nomme l'argument subscribe. Par conséquent, nous pourrions appeler la fonction transmise au constructeur comme la « fonction d'abonnement ».

    constructor(subscribe?: (this : Observable<T>, abonné: Abonné<T>)[19659027]=> TeardownLogic) {  
        if (subscribe) {  
          this._subscribe =subscribe;  
        }  
      }
    

    Comme nous le voyons, la fonction subscription prend un subscriber comme paramètre et renvoie une fonction contenant la logique de démontage. Le constructeur stocke la fonction d'abonnement dans une propriété de classe interne appelée _subscribe.

    La proposition TC39 nomme la fonction d'abonnement de la même manière—subscriber.

    La fonction d'abonnement/abonné est très important pour deux raisons :

    1. Il définit ce que l'instance observable ferait, c'est-à-dire qu'il définit comment produire des données et envoyer des données et des notifications à l'abonné (observateur).
    2. C'est la fonction qui est exécutée lorsque nous souscrivons à l'instance observable.

    La fonction observable

    Pour éviter de confondre la « fonction d'abonnement » avec la méthode observable class' subscribe()dans la suite de cet article nous nous référerons à la fonction que nous passons au constructeur observable sous le nom de « fonction observable. Alors que l'appeler la fonction d'abonnement met en évidence que cette fonction est invoquée lorsque nous souscrivons à l'observable.

    En quoi la fonction observable est-elle différente des autres fonctions ?

    Une fonction prend généralement une entrée, agit sur l'entrée et renvoie une valeur unique.

    Une fonction observable est une fonction d'ordre supérieur qui :

    • prend un objet abonné en entrée (l'objet abonné contient les fonctions de rappel)
    • produit des données
    • envoie une séquence des valeurs, une notification d'erreur ou une notification d'achèvement à l'abonné en appelant ses fonctions de rappel correspondantes
    • renvoie éventuellement une fonction de démontage

    Maintenant que nous avons vu que « fonction d'abonnement », « fonction d'abonné » et « fonction observable » sont tous les noms que nous pouvons appeler la fonction que nous passons au constructeur observable et avons parlé de ce qu'il fait, parlons de la façon dont les abonnés se rapportent aux observateurs.

    Séquence de valeurs

    Nous avons dit t qu'un observable puisse émettre de zéro à plusieurs valeurs. Mais comment un observable émet-il plusieurs valeurs ?

    La fonction observable peut appeler le rappel next() plusieurs fois, elle peut donc émettre une séquence de valeurs. Étant donné que l'observable peut émettre une séquence de valeurs au fil du temps, il est également appelé flux de données .

    Le nombre de valeurs dans la séquence dépend de l'instance observable. Un observable peut faire l'une de ces actions :

    • produire une seule valeur, puis terminer
    • produire plusieurs valeurs avant de terminer
    • continuer à produire des valeurs jusqu'à ce que nous lui disions d'arrêter en vous désabonnant
    • ne produire aucune valeur du tout

    Synchrone ou asynchrone

    Les observables appellent-ils les rappels d'observateur de manière synchrone ou asynchrone ?

    Afin de répondre à cette question, nous devons comprendre ce que signifie appeler une fonction de manière asynchrone.

    Veuillez lire l'accompagnement. article "Angular Basics : Introduction to Processes and Threads for Web UI Developers" pour en savoir plus sur les processus et les threads et la programmation asynchrone.

    Vous trouverez ci-dessous une explication rapide pour plus de commodité.

    Thread principal du Processus de rendu

    Les navigateurs modernes ont une architecture multiprocessus. Au lieu de tout exécuter en un seul processus, les navigateurs créent plusieurs processus pour prendre en charge différentes parties du navigateur.

    Les navigateurs ont généralement un processus distinct pour le rendu des pages Web.

    Le fil principal du processus de rendu est responsable de :

    • rendant la page Web
    • exécutant le JavaScript de l'application (à l'exception des travailleurs)
    • répondant aux interactions des utilisateurs

    Notre code d'application comprend JavaScript et des API Web. Nous utilisons des API Web (également appelées API de navigateur) pour fournir une variété de fonctionnalités permettant d'améliorer notre application Web.

    Les API de navigateur sont intégrées à votre navigateur Web et peuvent exposer les données du navigateur et environnement informatique environnant et faire des choses complexes utiles avec lui. —MDN

    Le JavaScript de notre application (à l'exception des travailleurs) s'exécute sur le thread principal du processus Renderer dans le navigateur. Les appels aux API Web peuvent s'exécuter sur un autre processus dans le navigateur. Un travailleur Web exécute le script sur un thread de travail dans le processus de rendu.

    Worker Threads

    Le code JavaScript qui prend trop de temps à s'exécuter bloque le thread principal du processus de rendu. C'est-à-dire que pendant que le thread principal attend le retour du code JavaScript, il ne peut pas mettre à jour le rendu ou répondre aux interactions de l'utilisateur. Cela a un impact négatif sur l'expérience utilisateur de notre application.

    Ne vous inquiétez pas cependant : nous pouvons décharger des fonctions de calcul intensif dans nos applications pour qu'elles s'exécutent sur des threads de travail en utilisant Web Workers API. Un thread de travail exécute le script et communique le résultat à l'application s'exécutant sur le thread principal en publiant un message. L'application dispose d'un événement onmessage pour traiter le résultat.

    API Web

    En plus d'empêcher le thread principal de se bloquer, nous pouvons utiliser des API Web pour accéder aux parties privilégiées d'un navigateur à partir de nos applications Web.

    Le processus de rendu d'un navigateur est généralement mis en bac à sable pour la sécurité. Cela signifie que le code de l'application Web ne peut pas accéder directement aux fichiers ou à la caméra de l'utilisateur, effectuer des requêtes réseau ou des appels au système d'exploitation, etc. À la place, nous utilisons des API Web fournies par les navigateurs pour accéder aux parties privilégiées d'un navigateur dans notre applications Web.

    Il est important de souligner que les appels à ces API Web ne sont pas exécutés sur le processus de rendu, mais sur un processus avec plus de privilèges tel que le processus de navigateur principal.

    Par exemple, nous pouvons utiliser le

    Récupérer l'API ou XMLHttpRequest pour demander des données au réseau. Dans Chrome, le thread réseau dans le processus du navigateur est responsable de la récupération des données sur Internet.

    Callbacks, Task Queues et Event Loop

    Les tâches effectuées sur un autre thread (autre que le thread principal du processus de rendu) sont des tâches asynchrones . Le processus/thread effectuant la tâche asynchrone communique avec le processus de rendu à l'aide de la communication inter-processus (IPC).

    Nous définissons des fonctions de rappel à exécuter une fois les tâches asynchrones terminées. Par exemple :

    setTimeout(() => console.log('Ceci est la fonction de rappel passée to setTimeout'), 1000);
    

    Le rappel traite tous les résultats renvoyés par la tâche asynchrone. Par exemple :

    
    
    navigator.geolocation.getCurrentPosition(console.log, console .warn);  
    

    Lorsqu'une tâche asynchrone est terminée, le thread effectuant la tâche asynchrone ajoute le rappel à une queue sur le thread principal du processus de rendu.

    Le processus de rendu comporte des files d'attente (file d'attente de travaux, file d'attente de tâches ou file d'attente de messages et file d'attente de micro-tâches) pour les rappels asynchrones prêts à être exécutés sur le thread principal. Le processus de rendu a également une boucle d'événement qui exécute les rappels en file d'attente lorsque la pile d'appels JavaScript est vide. La boucle d'événement exécute le rappel en file d'attente en passant n'importe quelle valeur renvoyée par la tâche asynchrone en tant qu'argument.

    Retour à la question : les observables appellent-ils les rappels d'observateur de manière synchrone ou asynchrone ? instance observable. Les observables peuvent émettre des données de manière synchrone ou asynchrone — cela dépend si la fonction observable exécute une tâche synchrone ou asynchrone pour produire des données.

    Ce n'est pas parce que les observables utilisent des rappels pour envoyer des données et des notifications que les rappels sont toujours exécutés de manière asynchrone— c'est-à-dire ajouté à une file d'attente de tâches ou de micro-tâches à exécuter par la boucle d'événements.

    Les observables peuvent émettre des données et des notifications de manière asynchrone

    Si la fonction observable exécute une tâche asynchrone pour produire des données, elle émet les données de manière asynchrone.

    Par exemple, un observable peut extraire des ressources du réseau à l'aide de l'Fetch API du navigateur :

    pikachu$ = new Observable( observer => {  
      fetch('https://pokeapi.co/api/v2/pokemon/pikachu')  
        .puis (réponse => réponse .json())  
        .puis(pikachu => {
          observateur.suivant(pikachu);
          observateur.complet();  
        })  
        .prise(err =>  observateur.erreur(err))  
    });
    
    pikachu$.souscrire({
      suivant: pikachu => console.log(pikachu),
      erreur: err => console.error(err)
    }) ; 
    

    La récupération de données sur le réseau est une tâche asynchrone qui est effectuée par un thread réseau. La méthode fetch() renvoie un objet promesse qui nous permet de traiter les résultats de la tâche asynchrone.

    Nous transmettons un rappel de réussite à l'objet promesse en appelant son then() méthode. Dans le rappel de réussite, nous émettons les données renvoyées par fetch en appelant observer.next(pikachu) et informons également l'observateur que nous avons fini d'envoyer des données en appelant observer.complete() .

    Nous transmettons également un rappel d'erreur à la promesse en appelant la méthode catch(). Dans le rappel d'erreur, nous informons l'observateur de l'erreur en appelant observer.error(err) et en transmettant les informations d'erreur. la boucle d'événement peut l'exécuter lorsque la pile d'appels est vide. Ainsi, les méthodes d'observation (next et completeou error) sont appelées de manière asynchrone dans cet observable.

    Les observables peuvent émettre des données et des notifications de manière synchrone[19659012]Les observables peuvent également émettre des données et des notifications de manière synchrone.
    const colourPalette$ = new Observable(observer => {[19659313]const palette = [
        'hsl(216,87%,48%)', 
        'hsl(216,87%,48%)'[19659018], 
        'hsl(42,99%,52%)', 
        'hsl(7,66%,49%)'
      ];
      pour[19659023](let couleur de palette) {
        observateur.suivant(couleur);
      }
      observateur.complet();
    }
    
    colourPalette$.subscribe(console.log);
    

    La fonction observable ci-dessus produit des données de manière synchrone. C'est-à-dire qu'il affecte un tableau de valeurs de chaîne à la constante palette (qui est la source de données). Il appelle ensuite observer.next(color) pour chaque couleur de la palette, puis appelle le rappel observer.complete()et revient enfin.

    Quand nous appelons next() dans cette instance observable, le moteur JavaScript crée un contexte d'exécution pour la fonction et l'ajoute à la pile d'appels. Aucune file d'attente ou boucle d'événement n'est impliquée.

    Observables froids contre chauds

    L'observable pourrait vraiment obtenir ses données de n'importe quelle source. Il pourrait obtenir des données de diverses API Web, telles que des événements DOM, des Websockets, Fetch ou Geolocation. Il pourrait boucler sur un itérable, ou même envoyer des valeurs codées en dur comme nous le faisons souvent dans les articles de blog et les didacticiels. 😊

    Le code responsable de la production de données pour un observable est la partie productrice réelle de l'observable. Il est important de souligner que nous pourrions définir le producteur dans le corps de la fonction observable ou référencer un producteur qui a été défini en dehors du corps observable.

    Un observable froid contient le code pour produire des données, tandis qu'un observable chaud se referme dessus.

    Regardons ensuite de plus près les observables froids et chauds.

    Observables froids

    Les caractéristiques des observables froids découlent des données produites dans le cadre de la fonction observable. ne produisons pas de données tant que nous n'avons pas souscrit. Lorsque nous souscrivons à un observable, il exécute la fonction observable. Étant donné que le code du producteur est inclus dans la fonction observable, il ne s'exécute que lorsque la fonction observable est appelée.

  • Les observables froids sont en monodiffusion. Chaque abonnement exécute la fonction observable et donc le code pour produire les données. Par exemple, si l'observable crée une instance d'un objet ou une valeur aléatoire, chaque observateur obtiendra sa propre instance distincte ou sa valeur unique.

Les observables que nous avons créés jusqu'à présent dans cet article sont des observables froids. Essayons d'en créer d'autres, en gardant à l'esprit cette fois que le code de production des données fait partie de la fonction observable.

Exemple 1 : Un observable à froid utilisant l'API de géolocalisation pour obtenir l'emplacement actuel de l'appareil de l'utilisateur et émettre l'emplacement à son observateur.

import { Observable } from 'rxjs';

const  location$ = new Observable(observer => {  
  let watchId;
  const  succès = poste => {
    observateur.next(position);  
  };
  const error = err => {
    observateur.error(err);  
  };
  const géolocalisation = `navigator .geolocation;`
  if (!geolocation) {
    observer.error('La géolocalisation n'est pas prise en charge par votre navigateur');  
  } else {
    watchId = géolocalisation.watchPosition(success, error);  
  }
  return[19659023]() => géolocalisation.clearWatch(watchId);
})[19659018];

Données : La position actuelle de l'appareil de l'utilisateur.

Producteur : navigator.geolocation.watchPosition().

Explication du code :
L'API de géolocalisation permet à l'utilisateur de fournir sa localisation aux applications Web s'il le souhaite. Pour des raisons de confidentialité, il est demandé à l'utilisateur l'autorisation de signaler les informations de localisation.

navigator.geolocation.watchPosition() effectue un rappel de réussite, un rappel d'erreur facultatif et des options.

Lorsque watchPosition() a localisé avec succès la position de l'appareil de l'utilisateur, il appellera le rappel de réussite et transmettra la position. Nous émettons la position de l'utilisateur dans le rappel de réussite. watchPosition() exécutera le rappel de réussite chaque fois qu'il aura une position mise à jour. Par conséquent, la fonction observable continuera à émettre la position mise à jour.

D'un autre côté, il pourrait y avoir une erreur, telle que l'API de géolocalisation n'existe pas sur le navigateur de l'utilisateur ou l'utilisateur a refusé l'autorisation de signaler ses informations de localisation. Nous informons l'utilisateur de l'erreur en appelant observer.error(err).

location$ est une observable froide puisqu'elle définit son producteur au sein de l'observable. Il ne commencera à produire et à émettre des valeurs que lorsque nous y souscrirons. Chaque observateur créera une nouvelle montre. Lorsqu'un observateur se désabonne, il ne désenregistrera que ses propres gestionnaires de succès et d'erreurs. Objet .

import { Observable } from 'rxjs';

const randomNumberCold$ =[19659016]new Observable(observer => {  
  const random = Math.random ();
  observer.next(random);  
  observer.complete();  
});

Data: a random number.

Producer: Math.random().

Each observer gets a separate random value since each subscription executes Math.random():

randomNumberCold$.subscribe(console.log); 
randomNumberCold$.subscribe(console.log); 

Hot Observable

Hot observables emit data that was produced outside the observable function body.

The data is generated independently of whether an observer subscribes to the observable or not. The observable function simply accesses the data that is already produced (outside the function) and emits the data to observers.

All the observers will get the same data. Thus, a hot observable is said to be multicast.

For example, here’s the random number example rewritten as a hot observable.

const random = Math.random();
console.log(random); 

const randomNumberHot$ = new Observable(observer => {  
  observer.next(random);  
  observer.complete();  
});

The random number is generated independently of our subscriptions to randomNumberHot$. You’ll notice that we haven’t subscribed to observable yet.

Each observer randomNumberHot$ gets the same random number because Math.random() is only executed once.

randomNumberHot$.subscribe(console.log); 
randomNumberHot$.subscribe(console.log); 

Built-in Observable Creation Functions in RxJS

So far in this article, we have created observables from scratch. That is, we used the new operator on the observable constructor and passed the observable function as an argument. We defined the body of the observable in the observable function.

However, we have hard-coded values in the observable function. How can we make the observables customizable and reusable?

You’re probably thinking, Hey, functions are customizable and reusable—we should use functions. Well, that’s a brilliant idea. 🦊 We can create functions that accept parameters, create a new observable based on these parameters, and return the observable instance.

The good news is that RxJS provides observable creation functions for most tasks so we don’t need to write them ourselves.

Let us look at some of the commonly used observable creation functions provided by RxJS:

  • from() expects an array, an array-like object, a promise, an iterable object or an observable-like object as its parameter. And it returns an observable that emits the items from the given input as a sequence of values.
from([5, 50, 100]).subscribe(console.log);



  • of() expects multiple parameters and creates an observable that emits each parameter as a value, then completes.
of([5, 50, 100], [10, 100, 200]).subscribe(console.log);


You may also be interested to learn about generate() and range().

Events

  • fromEvent() expects a target and event name as its parameters and returns an observable that emits the specified event type from the given target.
import { fromEvent } from 'rxjs';

const drag$ = fromEvent(document, 'drag');
drag$.subscribe(console.log);
const drop$ = fromEvent(document, 'drop');
drop$.subscribe(console.log);

You may also be interested to learn about fromEventPattern().

Timers

  • The interval() observable creation function returns an observable that emits the next number in the sequence at the specified interval.
import  { interval } from 'rxjs';

const seconds$ = interval(1000);
seconds$.subscribe(console.log);

const minutes$ = interval(60000);
minutes$.subscribe(console.log);

You may also be interested to learn about timer().

Creating Observables Dynamically

  • defer() allows us to create an observable only when the observer subscribes.

Combining Observables

You may also be interested to learn about splitting an observable using the partition() function.

Please refer to the RxJS docs for detailed explanations of the observable creation functions. If curious, you can also look at the implementation for a few of these functions.

Tune in to the Next Part

Next time we’ll talk about the process of subscribing to an observable, and unsubscribing vs. completing.






Source link