ANG_FRONT

Práctica 11. Funciones asincrónicas y diferencias con funciones síncronas

Objetivo de la práctica

Duración aproximada

Instrucciones

  1. Función síncrona. ``` javascript

function suma(a, b) { return a + b; }

console.log(“Inicio”); console.log(suma(2, 3)); // Salida: 5 console.log(“Fin”);


- Resultado Esperado:


```console
Inicio
5
Fin
  1. Funciones asincrónicas.

Promesa


function obtenerDatos() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Datos obtenidos");
    }, 2000);
  });
}

console.log("Inicio");
obtenerDatos().then((resultado) => {
  console.log(resultado); // Salida: Datos obtenidos
});
console.log("Fin");

Inicio
Fin
Datos obtenidos
  1. Async/Await

async function ejecutar() {
  console.log("Inicio");
  const resultado = await obtenerDatos();
  console.log(resultado); // Salida: Datos obtenidos
  console.log("Fin");
}

ejecutar();

Inicio
Datos obtenidos
Fin

Promesas en JavaScript

  1. Creación de una promesa.
    • Crear una promesa utilizando el constructor Promise, que toma una función llamada “executor” con dos argumentos: resolve y reject.

const miPromesa = new Promise((resolve, reject) => {
  const exito = true; // Simulación de una condición

  if (exito) {
    resolve("Operación exitosa");
  } else {
    reject("Error en la operación");
  }
});

  1. Consumiendo promesas.

then(): Se ejecuta cuando la promesa es cumplida. catch(): Se ejecuta cuando la promesa es rechazada.


miPromesa
  .then((resultado) => {
    console.log(resultado); // Salida: Operación exitosa
  })
  .catch((error) => {
    console.error(error); // En caso de error
  });

  1. Encadenamiento de promesas.

const promesaEncadenada = new Promise((resolve) => {
  resolve(5);
});

promesaEncadenada
  .then((resultado) => resultado * 2)
  .then((resultado) => resultado + 3)
  .then((resultadoFinal) => {
    console.log(resultadoFinal); // Salida: 13
  });
  1. Promesas combinadas.

const promesa1 = Promise.resolve(3);
const promesa2 = new Promise((resolve) => setTimeout(resolve, 100, '¡Hola!'));
const promesa3 = 42;

Promise.all([promesa1, promesa2, promesa3])
  .then((resultados) => {
    console.log(resultadost); // Salida: [3, "¡Hola!", 42]
  })
  .catch((error) => {
    console.error("Una de las promesas falló:", error);
  });

  1. Promise.race() - Este método también toma un array de promesas, pero devuelve una nueva promesa que se resuelve o se rechaza tan pronto como una de las promesas se cumple o se rechaza.

const promesaLenta = new Promise((resolve) => setTimeout(resolve, 500, 'Lenta'));
const promesaRapida = new Promise((resolve) => setTimeout(resolve, 100, 'Rápida'));

Promise.race([promesaLenta, promesaRapida])
  .then((resultado) => {
    console.log(resultado); // Salida: "Rápida"
  });
  1. Manejo de Errores

const promesaConError = new Promise((resolve, reject) => {
  reject("Hubo un error");
});

promesaConError
  .then((resultado) => {
    console.log(resultado);
  })
  .catch((error) => {
    console.error(error); // Salida: Hubo un error
  });

Patrón Observer

  1. Diagrama del Patrón

+——————+ +——————+ | Subject |<————->| Observer | +——————+ +——————+ | +attach(o:Observer)| | +update() | | +detach(o:Observer)| +——————+ | +notify() | | +getState() | | +setState() | +——————+ | | +———————+ | ConcreteSubject | +———————+ | +getState() | | +setState() | +———————+ | | +———————+ | ConcreteObserver | +———————+ | +update() | +———————+

  1. Implementación del Patrón Observer en JavaScript

    1. Paso 1: Definir el sujeto.
      • Crear la clase Subject que gestiona la lista de observadores y proporciona métodos para añadir, eliminar y notificar observadores.

class Subject {
  constructor() {
    this.observers = [];
  }

  attach(observer) {
    this.observers.push(observer);
  }

  detach(observer) {
    this.observers = this.observers.filter(obs => obs !== observer);
  }

  notify() {
    this.observers.forEach(observer => observer.update(this));
  }
}
  1. Paso 2: Definir los observadores. - Definir la clase Observer, que se encargará de manejar las actualizaciones.

class Observer {
  update(subject) {
    console.log(`Estado actualizado: ${subject.getState()}`);
  }
}
  1. Paso 3: Implementar el sujeto concreto. - Crear una clase ConcreteSubject que extiende a Subject y añadir funcionalidad para gestionar su estado.

class ConcreteSubject extends Subject {
  constructor() {
    super();
    this.state = 0;
  }

  getState() {
    return this.state;
  }

  setState(state) {
    this.state = state;
    this.notify(); // Notificar a los observadores sobre el cambio de estado
  }
}
  1. Paso 4: Implementar observadores concretos. - Definir una clase ConcreteObserver que implementa la lógica de reacción al cambio de estado.

class ConcreteObserver extends Observer {
  constructor(name) {
    super();
    this.name = name;
  }

  update(subject) {
    console.log(`${this.name} ha recibido la actualización: ${subject.getState()}`);
  }
}
  1. Uso del Patrón Observer

const subject = new ConcreteSubject();

const observerA = new ConcreteObserver('Observer A');
const observerB = new ConcreteObserver('Observer B');

subject.attach(observerA);
subject.attach(observerB);

subject.setState(1); // Salida: Observer A ha recibido la actualización: 1
                     //         Observer B ha recibido la actualización: 1

subject.detach(observerA);

subject.setState(2); // Salida: Observer B ha recibido la actualización: 2

  1. Ventajas del Patrón Observer.
  1. Desventajas del Patrón Observer.

Fundamentos de la Programación Reactiva

  1. Observable
    • Un observable es la pieza central de la programación reactiva. Permite a los desarrolladores definir flujos de datos que pueden ser observados. En JavaScript, bibliotecas como RxJS ofrecen implementaciones de observables.

import { Observable } from ‘rxjs’;

const miObservable = new Observable(subscriber => { subscriber.next(‘Hola’); subscriber.next(‘Mundo’); subscriber.complete(); });

miObservable.subscribe({ next(x) { console.log(x); }, complete() { console.log(‘Completado’); } });

- Salida Esperada:


```consola
Hola
Mundo
Completado
  1. Suscripciones.
    • Una vez que un observable es creado, se pueden hacer suscripciones para recibir datos.

const subscription = miObservable.subscribe({ next(x) { console.log(x); } });

// Para cancelar la suscripción subscription.unsubscribe();


## peradores Comunes en Programación Reactiva

- RxJS incluye una amplia variedad de operadores que permiten manipular flujos de datos.

1. map
- Transformar los datos emitidos por un observable.

```javascript

import { map } from 'rxjs/operators';

const numeros = [1, 2, 3];
const numerosObservable = from(numeros).pipe(
  map(num => num * 2)
);

numerosObservable.subscribe(console.log); // Salida: 2, 4, 6

  1. filter
    • Filtrar los datos emitidos según una condición.

import { filter } from 'rxjs/operators';

const numerosFiltrados = from(numeros).pipe(
  filter(num => num > 1)
);

numerosFiltrados.subscribe(console.log); // Salida: 2, 3
  1. combineLatest
    • Combinar múltiples observables y emitir el último valor de cada uno.

import { combineLatest } from 'rxjs';

const observable1 = of(1, 2, 3);
const observable2 = of('A', 'B', 'C');

combineLatest([observable1, observable2]).subscribe(console.log);
// Salida: [3, "C"] (últimos valores de ambos observables)

RxJS y Observables

  1. Usando new Observable()
    • Este método permite definir un observable desde cero.

import { Observable } from 'rxjs';

const miObservable = new Observable(subscriber => {
  subscriber.next('Hola');
  subscriber.next('Mundo');
  subscriber.complete();
});

  1. Usando métodos de creación.
    • RxJS ofrece métodos de creación como of, from, interval, y timer.

import { of } from 'rxjs';

const numeros$ = of(1, 2, 3, 4, 5);

import { from } from 'rxjs';

const array$ = from([10, 20, 30]);

import { interval } from 'rxjs';

const contador$ = interval(1000); // Emite un número cada segundo
  1. Suscripciones.

const subscription = miObservable.subscribe({
  next(x) { console.log(x); },
  complete() { console.log('Completado'); }
});

// Para cancelar la suscripción
subscription.unsubscribe();

Operadores de RxJS

  1. Transformación de datos.

import { map } from 'rxjs/operators';

numeros$.pipe(
  map(num => num * 2)
).subscribe(console.log); // Salida: 2, 4, 6, 8, 10

  1. Filtrado de datos.

import { filter } from 'rxjs/operators';

numeros$.pipe(
  filter(num => num > 2)
).subscribe(console.log); // Salida: 3, 4, 5
  1. Combinación de observables

import { merge } from 'rxjs';

const obs1$ = of('A', 'B');
const obs2$ = of('C', 'D');

merge(obs1$, obs2$).subscribe(console.log); // Salida: A, B, C, D

Ejercicio practico

  1. Configuración del proyecto.

ng new buscador-app
cd buscador-app
ng serve

  1. Instalación de dependencias.
  1. Creación del servicio de búsqueda.

import { Injectable } from '@angular/core';
import { of } from 'rxjs';
import { delay } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class ApiService {
  private datos = ['Angular', 'React', 'Vue', 'Svelte', 'Ember'];

  buscar(termino: string) {
    const resultados = this.datos.filter(item =>
      item.toLowerCase().includes(termino.toLowerCase())
    );
    return of(resultados).pipe(delay(500)); // Simula un retraso en la respuesta
  }
}
  1. Creación del componente de búsqueda

Crear un componente llamado busqueda.component.ts:


import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
import { debounceTime, switchMap } from 'rxjs/operators';
import { ApiService } from './api.service';

@Component({
  selector: 'app-busqueda',
  template: `
    <input [formControl]="searchControl" placeholder="Buscar..."/>
    <ul>
      <li *ngFor="let resultado of resultados"></li>
    </ul>
  `,
  styles: [`
    input {
      margin: 20px 0;
      padding: 10px;
      width: 300px;
    }
    ul {
      list-style: none;
      padding: 0;
    }
    li {
      padding: 5px 0;
    }
  `]
})
export class BusquedaComponent implements OnInit {
  searchControl = new FormControl();
  resultados: string[] = [];

  constructor(private apiService: ApiService) {}

  ngOnInit() {
    this.searchControl.valueChanges.pipe(
      debounceTime(300), // Espera 300ms antes de buscar
      switchMap(value => this.apiService.buscar(value)) // Llama al servicio de búsqueda
    ).subscribe(resultados => {
      this.resultados = resultados; // Actualiza los resultados en la vista
    });
  }
}

  1. Actualización del módulo.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { BusquedaComponent } from './busqueda.component'; // Importa el componente

@NgModule({
  declarations: [
    AppComponent,
    BusquedaComponent // Declara el componente
  ],
  imports: [
    BrowserModule,
    ReactiveFormsModule // Agrega el módulo de formularios reactivos
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}
  1. Integración en la plantilla principal.

<h1>Buscador en Tiempo Real</h1>
<app-busqueda></app-busqueda>
  1. Ejecución del Proyecto.

ng serve