freeCodeCamp/guide/spanish/angular/reactive-extensions/index.md

18 KiB

title localeTitle
Reactive Extensions Extensiones reactivas

Extensiones reactivas en angular

Motivación

Las extensiones reactivas para JavaScript (RxJS) son una biblioteca para flujos de datos observables . RxJS se instala con Angular en la ejecución de la línea de comandos de ng new [name-of-application] . Esto utiliza la interfaz de línea de comandos angular (CLI). RxJS complementa cómo se simplifican los datos a través de un Observable . El objeto Observable facilita el flujo de datos iterables .

Los flujos de datos no son el caso de uso principal. Después de todo, los flujos de datos son flujos de eventos paralelos. Los eventos se emiten para que una aplicación sepa cuándo llegan los datos. Si bien las secuencias de eventos forman el núcleo de lo que RxJS complementa, este artículo también se refiere a ellas como secuencias de datos.

Las secuencias se ejecutan de forma síncrona (inmediata) o asíncrona (horas extras). RxJS maneja ambos casos con facilidad a través del flujo de datos Observable . Sin embargo, la asincronía estricta se puede cambiar. Al trabajar con la memoria, los datos iterables se producen inmediatamente, mientras que la obtención de datos externos lleva tiempo. Angular es compatible con la biblioteca RxJS para que pueda manejar ambos casos de uso con flujos de datos.

Programación reactiva

Antes de sumergirse, es importante comprender el paradigma que respalda la biblioteca RxJS. Como se mencionó, funciona a través del objeto Observable que optimiza los datos que emiten eventos.

RxJS funciona alrededor de la base Observable . Toda su biblioteca complementa lo que puede hacer. RxJS incluso incluye otros objetos, incluyendo Subject , Subscription y Observer . Cada uno es su propia variante personalizada de la base Observable .

RxJS surgió del paradigma de la programación reactiva. Este paradigma introdujo el patrón observable . En él existe esta idea clave: un único Observable emite mientras todos sus Observer reciben una notificación. Observer suscriben a los Observable para recibir una notificación. Esta notificación puede significar varias cosas.

Podría indicar un cambio en los datos o la llegada de datos como se indica comúnmente en este artículo. Podría indicar un cambio en alguna parte de la aplicación que afecta a los Observer .

Este patrón observable también se esfuerza por desacoplar los conceptos. Un Observable debe poder funcionar sin ningún Observer y viceversa. Por lo general, esto significa que pueden ser independientes en lugar de funcionar completamente sin el otro.

Si es curioso, puede aprender más sobre los fundamentos de la Programación reactiva leyendo este artículo de Wikipedia . Esta sección cubre lo que es necesario para el resto del artículo.

Observables

Para reiterar rápidamente, Observable pueden observarse s. Esto para que un Observable proporcione retroalimentación a sus dependencias en base a un flujo de datos. En RxJS, el Observable es su propia función de fábrica utilizada para crear objetos Observable . Su plano básico es el siguiente.

import { Observable } from 'rxjs'; 
 
 const observable = Observable.create((source) => { 
  source.next(data); 
 }); 

.next pasa datos mientras emite un evento a sus observadores. Un Observable emite datos desde dentro de su .create callback usando .next . Acepta un argumento que representa los datos a emitir. El Observable no se ha implementado en JavaScript todavía. RxJS proporciona un reemplazo de su biblioteca.

El siguiente paso son los observadores. Para decirle a una función u objeto que observe un Observable , se usa la siguiente sintaxis: observable.subscribe(observer) . Otra forma de verlo es producer.subscribe(consumer) . Los observables producen datos llamando a .next . Los consumidores son notificados mientras reciben los datos.

import { Observable } from 'rxjs'; 
 
 const observable = Observable.create((source) => { 
  source.next("Hello"); 
  source.next("World!"); 
 }); 
 
 observable.subscribe((word) => console.log(word)); 
 // console output 
 /* 
 Hello 
 World! 
 */ 

Dos invocaciones de .next producen dentro de la devolución de llamada .create del Observable (productor de datos). Esto da como resultado dos salidas de consola separadas desde el observador (consumidor de datos).

Las dos invocaciones de .next representan un flujo síncrono de datos. Los flujos conceptualizan los datos como un flujo lineal, en orden. Se resuelve de forma síncrona o asíncrona según la disponibilidad de los datos.

Si los datos que comprenden un flujo están disponibles, se ejecutan de forma síncrona. De lo contrario, el flujo se resuelve de forma asíncrona en el tiempo extra. El orden de los datos en ambos casos es siempre el mismo, dependiendo de la invocación de .next dentro del observable.

Un Observable opera como una cola. Llamar a .next en un dato lo empuja al final de la cola. Los datos aparecen desde el frente una vez resueltos.

Observable flujos de datos Observable tienen un gran atractivo. Son deterministas en su orden y se ejecutan sensiblemente en función de la disponibilidad de los datos. Además, cualquier número de observadores puede observar la fuente de datos Observable . Esto significa que los datos pueden producirse una vez y emitirse en cualquier lugar, todo en una operación.

Las funciones de devolución de llamada no son la única forma de consumir datos. Los observables pueden encadenarse entre sí como productores y consumidores.

const observableI = Observable.create((source) => { 
  source.next("Hello World!"); 
 }); 
 
 const observableII = new Observable().subscribe((v) => console.log(v)); 
 
 observableI.subscribe(observableII); 
 // console output 
 /* 
 Hello World! 
 */ 

.subscribe está en el objeto Observable . Puede llamarlo con un Observable como su origen (productor) y otro observable como su argumento (consumidor). Los datos pueden fluir (emitir) a través de cualquier número de observables.

Extensiones reactivas para JavaScript (RxJS)

La transmisión de datos es agradable, pero ¿cuál es el punto si los observables no pueden editar la transmisión? Aquí es donde entra en juego la biblioteca RxJS. Proporciona operadores que realizan varias mutaciones en el flujo de datos.

Angular aprovecha estos operadores para transformar los datos entrantes. Los desarrolladores pueden recortar cualquier dato innecesario de un flujo entrante utilizando operadores RxJS. Esto ahorra memoria y alivia la necesidad de una lógica de transformación adicional.

RxJS ofrece desviaciones del Observable estándar como Subject , Subscription y Observer . Piense en estas desviaciones como sabores especiales de los Observable tradicionales. No son necesarios para hacer uso de la biblioteca. Dicho esto, las variantes como Subject tienen casos de uso increíbles que superan el estándar Observable .

Este artículo se mantiene con el estándar Observable . Todos los operadores de flujo de datos de RxJS trabajan a través del Observable .

Muchos operadores RxJS principales se originaron a partir de Array Extras de JavaScript. El prototipo del objeto Array contiene muchos paralelismos con la biblioteca RxJS. Estos son también conocidos como 'Extras'. Las matrices son estructuras similares a corrientes similares a los vapores de datos observables.

Para comprender mejor a los operadores de RxJS, este artículo tratará brevemente los Array Extras de JavaScript. Cada uno funciona de forma casi idéntica a su contraparte RxJS. La diferencia es simplemente el formato de los datos (matriz iterable vs flujo iterable).

Array Extras

Las matrices contienen muchos métodos de utilidad. Estos métodos se llaman Array Extras. Todos ellos existen en el prototipo del objeto Array. La siguiente lista contiene cinco Extras con paralelos RxJS.

  • .reduce
  • .filter
  • .map
  • .every
  • .forEach

Para cada ejemplo, la matriz se itera sobre sí misma para producir un resultado final.

.reduce minimiza una matriz en un solo valor. .filter recorta una matriz con evaluación booleana. .map transforma una matriz elemento por elemento. .every evalúa verdadero o falso para toda la matriz en función de una condición booleana. .forEach itera a través de los elementos de una matriz.

Arreglos de arroyos modelo. Están en orden unos de otros y se repiten uno por uno. Los observables racionalizan los elementos de datos a sus observadores de una manera similar. Esta es la razón por la que RxJS incluye una contraparte lógica para cada Array Extra en su biblioteca. Por supuesto, RxJS proporciona muchos más operadores propios que Array Extras.

Operadores RxJS básicos

Hay literalmente el valor de una biblioteca completa de operadores RxJS. Este artículo se centra en los que se enumeran a continuación, en los que se inspiró el Array Extras.

  • .reduce
  • .filter
  • .map
  • .every
  • .forEach

Nada ha cambiado desde la lista anterior. Su comprensión de Array Extras se aplica a los operadores de RxJS. La única pega a esto es una función llamada .pipe que verá mucho uso en los siguientes ejemplos. .pipe encadena a los operadores de RxJS. Los resultados del operador anterior continúan en el siguiente hasta el operador final. Los datos resultantes luego se emiten desde el flujo observable.

Tenga en cuenta el ejemplo estándar a continuación. Úselo para comparar el impacto de cada operador en el flujo de datos emitido.

import { Observable, from } from 'rxjs'; 
 
 const stream: number[] = [1, 2, 3, 4, 5]; 
 
 const observable: Observable<number> = from(stream); 
 observable.subscribe((val: number) => console.log(val)); 
 
 // console output 
 /* 
 1 
 2 
 3 
 4 
 5 
 */ 

.from convierte una matriz en un objeto Observable que llama a .next en cada elemento de la matriz. La función .pipe acepta cualquier número de argumentos como operadores de matriz. Aquí es donde cada operador se implementa. Los operadores ejecutan en datos optimizados en orden de su implementación como argumentos para .pipe .

Reducir

.reduce minimiza el flujo de datos en un solo valor antes de emitir.

import { reduce } from 'rxjs/operators'; 
 
 const stream: number[] = [1, 2, 3, 4, 5]; 
 
 const observable: Observable<number> = from(stream).pipe( 
  reduce((accum, val) => (accum + val)) 
 ); 
 observable.subscribe((val: number) => console.log(val)); 
 
 // console output 
 /* 
 15 
 */ 
Filtrar

.filter recorta un flujo, eliminando los valores de flujo que no satisfacen su condición.

import { filter } from 'rxjs/operators'; 
 
 const stream: number[] = [1, 2, 3, 4, 5]; 
 
 const observable: Observable<number> = from(stream).pipe( 
  filter((val) => (val % 2 === 0)) // filters out odd numbers 
 ); 
 observable.subscribe((val: number) => console.log(val)); 
 
 // console output 
 /* 
 2 
 4 
 */ 
Mapa

.map apunta y transforma cada valor de flujo continuo.

const stream: number[] = [1, 2, 3, 4, 5]; 
 
 const observable: Observable<number> = from(stream).pipe( 
    map((val) => (val + 1)) 
 ); 
 observable.subscribe((val: number) => console.log(val)); 
 
 // console output 
 /* 
 2 
 3 
 4 
 5 
 6 
 */ 
Desafío: cada y para cada

Con el conocimiento de .every y .forEach como extras de matriz, tratar de ponerlas en práctica ya que los operadores RxJS. Siéntase libre de usar los ejemplos anteriores para orientación. ¡Un poco de práctica va muy lejos después de mucha lectura!

HTTP en Angular

Esta sección reúne a RxJS y Angular para mostrar cómo interactúan. Típicamente, un servicio provisto por Angular proporcionará el Observable . El flujo de datos del Observable puede luego mutar utilizando operadores .pipe con .pipe .

Angular proporciona un servicio HttpClient través de @angular/common/http . HttpClientModule también es de @angular/common/http y exporta el servicio HttpClient . El módulo raíz de la aplicación debe importar HttpClientModule . Esto hace que HttpClientModule sea inyectable desde cualquier lugar de la aplicación.

El servicio HttpClientModule realiza solicitudes HTTP. Estas solicitudes son asíncronas. Lo que los hace interesantes para Angular es cómo se maneja la solicitud. Un Observable se devuelve con cada solicitud. RxJS puede quitarlo de allí.

El próximo ejemplo utiliza una API pública construida por Typicode . La API proporciona una matriz de 100 elementos por solicitud GET asíncrona.

// ./models/post.model.ts 
 
 export interface Post { 
  userId: number; 
  id: number; 
  title: string; 
  body: string; 
 } 
// ./services/json.service.ts 
 
 import { HttpClient } from '@angular/common/http'; 
 import { Injectable } from '@angular/core'; 
 
 import { Observable, from } from 'rxjs'; 
 import { switchMap, map, filter, reduce } from 'rxjs/operators'; 
 
 import { Post } from '../models/post.model'; 
 
 @Injectable({ 
  providedIn: 'root' 
 }) 
 export class JsonService { 
  constructor(private http: HttpClient) { } 
 
  getPostsByUserId(id: number): Observable<any> { 
    const trim$ = (stream) => from(stream) 
      .pipe( 
        filter((post: Post) => +post.userId === +id), 
        map((post: Post) => ({ title: post.title, body: post.body })), 
        reduce((accum: Post[], post: Post) => accum.concat([post]), []) 
      ); 
 
    return this.http.get("https://jsonplaceholder.typicode.com/posts") 
      .pipe( 
        switchMap((value) => trim$(value)) 
      ); 
  } 
 } 
// ./components/example/example.component.ts 
 
 import { Component } from '@angular/core'; 
 
 import { JsonService } from '../../services/json.service'; 
 import { Post } from '../../models/post.model'; 
 
 @Component({ 
  selector: 'app-example', 
  template: ` 
  <h1>Request User Posts</h1> 
  <span>User: </span><input #userInput> 
  <button (click)="requestForPosts(userInput.value)">REQUEST</button> 
  <hr> 
  <ul> 
    <div *ngIf="userPosts"> 
      <div *ngFor="let post of userPosts"> 
        <h3>{{ post.title }}</h3> 
        <p>{{ post.body }}</p> 
      </div> 
    </div> 
    <h3 *ngIf="!userPosts">No posts shown...</h3> 
  </ul> 
  ` 
 }) 
 export class ExampleComponent { 
  userPosts: Post[]; 
 
  constructor(private json: JsonService) { } 
 
  requestForPosts(id: number): void { 
    this.json.getPostsByUserId(id) 
      .subscribe((posts: Post[]) => { this.userPosts = posts.length > 0 ? posts : null; }); 
  } 
 } 

json.service.ts crea un Observable en nombre de component.example.ts . El componente puede suscribirse al Observable devuelto. Solo un valor se emite en el momento en que el Observable resuelve el flujo de datos.

La solicitud a jsonplaceholder.typicode.com produce una matriz única de 100 publicaciones. La solicitud a través de HttpClient produce un Observable . El operador switchMap devuelve otro Observable que sobrescribe la secuencia actual. La variable trim$ almacena el Observable como su valor. Agregar una $ a una variable utilizada para almacenar Observable s es una convención.

from convierte la matriz de jsonplaceholder.typicode.com en un Observable emite 100 valores. Los operadores de RxJS luego filtran cada parte de los datos en el flujo. Se eliminan los valores de flujo irrelevantes para la solicitud. El recorte de datos se lleva a cabo para que los valores de flujo se mantengan escasos de información innecesaria. Los resultados finales se unen una vez más como una única matriz que emite uno a sus observadores.

En component.example.ts , JsonService hace referencia al método que devuelve el Observable recién descrito. Este método invoca al hacer clic en el botón en la plantilla del componente. El cuadro de entrada también en la plantilla proporciona el argumento de id único.

Con el botón presionado, JsonService devuelve un Observable que emite una única matriz. .subscribe invoca en el Observable devuelto. El componente luego establece el valor de userPosts igual a la matriz emitida.

La Detección de Cambio Angular recoge el cambio en los datos de clase. La plantilla se actualiza y *ngFor garantiza que cada elemento de la matriz de userPosts su propio elemento de plantilla.

Conclusión

RxJS proporciona el núcleo Observable junto con sus operadores. La biblioteca se instala automáticamente desde la línea de comandos usando ng new [name-of-app] (CLI angular). Los tipos principales y operadores de rxjs descargan a rxjs y rxjs/operators , respectivamente.

Incluso si no utiliza la CLI, los servicios como HttpClient pueden seguir utilizándose. El servicio devuelve una Promise lugar de un Observable si RxJS no está disponible. El objeto Promise es nativo de JavaScript, a diferencia del Observable . Esto probablemente cambiará en la próxima versión oficial de JavaScript.

Dicho esto, ¡aprovecha al máximo RxJS! Cualquier estructura iterable puede acomodar al Observable . Con él, toda la biblioteca RxJS se vuelve utilizable. Sus operadores transforman eficientemente los datos de un flujo en resultados. Además, los observadores pueden suscribirse a los resultados, lo que aumenta la portabilidad general de los datos.

Fuentes

Recursos