Promesas en Javascript para React y TypeScript
Tags: react, reactnative, javascript, spanish
Published: 2022-09-15 16:09:08

Para empezar a hablar sobre promesas debemos conocer el concepto de asincronismo como ya lo sabemos es realizar acciones o tareas sin esperar que un proceso anterior se realice, algo así como en segundo plano para ejemplificar más fácil este concepto, entonces que es una promesa como dice el significado mismo de la palabra algo que esperamos que se cumpla en un futuro próximo aunque también puede no cumplirse y fallar.

Primero empecemos con un ejemplo de la vida cotidiana imaginemos que vamos a comprar comida 🍔 una hamburguesa está bien, llegamos al mostrador pedimos la hamburguesa, pagamos, nos dan un ticket con el cual debemos esperar y  quedaremos pendiente, mientras nos preparan la hamburguesa tomamos el celular quizá escribimos un mensaje o revisamos las redes sociales esperando, una vez esta lista, nos llaman, entregamos el ticket y recibimos lo que esperamos a cambio nuestra comida, un simple caso de éxito sin embargo también puede fallar puede que nos llamen y nos digan que las hamburguesas se terminaron, entonces ambos escenarios son posibles en esa simplicidad se basan las promesas aunque en javascript no es tan sencillo pero ya lo veremos a continuación

El por qué de las promesas? el código asíncrono tiene su forma de manejarse y debido a que pueden surgir eventos innesperados como (fallos de red) hay la necesidad de manejar errores por lo tanto usar solo callbacks o devoluciones de llamada puede ser muy complejo, las promesas tratan de solucionar este tema

Qué es una promesa en JavaScript es un objeto que devolverá un valor en el futuro




***Crear***

Quizá estemos desarrollando una API de lado del backend que se va a utilizar luego por lo que podemos crear una función que retorne una promesa y se pueda consumir de lado del front o en algún servicio en específico y asegurar un mejor manejo del flujo de los datos y los errores

Por lo tanto existen tres estados posibles en la creación de una promesa: 


- Éxito - resolver -> resolve(value) 
- Fracaso - rechazar -> reject (error)
- Pendiente (pending)


***Consumir***

Quizá estemos desarrollando una aplicación en el Fronted y tenemos que consumir APIs que retornan promesas, un ejemplo es la api de Fetch aunque puede ser cualquiera, entonces debemos conocer como estas funcionan para hacer el uso correctamente

Entonces para el consumo de una promesa existen los siguiente métodos conocidos como manejadores

- .then() -> si la promesa se resuelve con éxito
- .catch() -> si la promesa falla
- .finally() -> si la promesa ha sido ejecutada sea éxito o falla





Y bueno antes de entrar al código con todo este preámbulo lo más importante es saber para que las vas a usar en tu día a día programando en javascript y son dos usos esenciales, van de la mano pero puede que un día estemos de un lado pero al siguiente estemos del otro en sí es como back y front


**Sintaxis**
Para su creación se utiliza *new Promise*, recibe una función ejecutora que tiene dos callbacks *resolve* y *reject*

`new Promise( function (resolve, reject) { ... } ) ;`

*Creación*

```javascript
const promiseExample = new Promise( (resolve, reject) => {
      
     if (condition) {
       resolve("data ...");
    } else {
       reject( "error");
    }

});
```

*Consumo*

```
promiseExample.then( (data) => { ... } )
                            .catch( (err) => { ... } )

```


Ejemplo real

Tenemos un objeto que contiene información de autos de lujo vamos a crear una función que nos permita buscar estos autos por su id y retornar una promesa para us uso posterior

```javascript
const autos = {
 
   buggati: {
     nombre: 'Veyron'
     marca: 'Buggati'
   },

   camaro:{
     nombre: 'Camaro',
    marca: 'Chevrolet'
   },

  mustang:{
     nombre: 'Mustang',
     marca: 'Ford'
   }

}
```

```javascript
export const buscarAuto = ( id ) => {

  const auto = autos[id];

  return new Promise( (resolve, reject) =>{
    if(auto){
       resolve(auto);

    } else {
      reject(`No existe un auto con el ${id}`);
     }

});


}
```

Ahora vamos a usar la promesa quizá en otro archivo, en el que podemos importar la función

```javascript
const autoId1= 'buggati';

buscarAuto ( autoId1 )
.then( auto => { 
  console.log(`El auto ${auto.nombre} existe`);
}
.catch ( err => { 
    alert(err);
})


```



***Encadenamiento de promesas***

En ocasiones al consumir una promesa esta devuelve otra promesa y así puede pasar hasta n veces, por lo que nos vemos obligados a usar varios manjeadores `.then()` para poder obtener el resultado esperado un ejemplo es al usar el mecanismos `fetch`

En este ejemplo hacemos solicitud a una api y primero obtenemos el archivo json con los datos luego en estos datos obtenemos el que deseamos 

```javascript
fetch('api/encadenamiento-promesas/pokemon.json')
.then( response => response.json() )
.then( pokemon => console.log(pokemon.name) )

```

Y así podemos seguir encadenando promesas de acuerdo al requerimiento, en un próximo artículo veremos como solucionar esto con async/ await


Finalmente un ejemplo dentro de React y TypeScript y usando un método de las promesas *all*

En este caso se hacen varias peticiones a una API de Peliculas y se necesita crear un hook personalizado que combine useEffect y useState

Primero tenemos la creación de la instancia de axios para el consumo de la api

```javascript
const movieDB = axios.create({
   baseURL: 'https://api.themoviedb.org/3/movie',
   params: {
    api_key: 'xxxxxxx',
    language: 'es-ES',
}


});

export default movieDB
```


Luego en la creación de hook importamos las interfaces de las movies y creamos una interface para el Estado que tendran los diferentes tipos de peliculas a consumir 

```javascript 
import {Movie, MovieDBMoviesResponse} from '../interfaces/movieInterface';

interface MoviesState {
  nowPlaying: Movie[];
  popular: Movie[];
  topRated: Movie[];
  upcoming: Movie[];
}

```


Con esta interfaz creada podemos tipar las respuestas que tendrán el hook useState en las movies y el valor inicial

```javascript
  const [moviesState, setMoviesState] = useState<MoviesState>({
    nowPlaying: [],
    popular: [],
    topRated: [],
    upcoming: [],
  });

```

Ahora se creará una función para obtener el resultado de la consulta a los distintos enpoints usando la instancia de axios creada colocando los tipos correspondiente y pasando la ruta para cada una, luego agrupamos las promesas de cada petición y usamos el Promise.all para obtener los resultados y setear el estado

```javascript
  const getMovies = async () => {
    const nowPlayingPromie = movieDB.get<MovieDBMoviesResponse>('/now_playing');
    const popularPromise = movieDB.get<MovieDBMoviesResponse>('/popular');
    const topRatedPromise = movieDB.get<MovieDBMoviesResponse>('/top_rated');
    const upcomingPromise = movieDB.get<MovieDBMoviesResponse>('/upcoming');

    const resps = await Promise.all([
      nowPlayingPromie,
      popularPromise,
      topRatedPromise,
      upcomingPromise,
    ]);
    setMoviesState({
      nowPlaying: resps[0].data.results,
      popular: resps[1].data.results,
      topRated: resps[2].data.results,
      upcoming: resps[3].data.results,
    });

    setIsLoading(false);
  };
```

Llamamos la función getMovies con ayuda del hook useEffect
 
```javascript 
  useEffect(() => {
    getMovies();
  }, []);
``` 


y finalmente retornamos el un objeto con todos los resultados obtenidos en el movieState de los distintos arreglos de la peticiones realizadas a la API con ayuda del parámetro rest

```javascript
  return {
    ...moviesState,
    isLoading,
  };
```

Código completo

```javascript
import {useEffect, useState} from 'react';
import movieDB from '../api/movieDB';
import {Movie, MovieDBMoviesResponse} from '../interfaces/movieInterface';

interface MoviesState {
  nowPlaying: Movie[];
  popular: Movie[];
  topRated: Movie[];
  upcoming: Movie[];
}

export const useMovies = () => {
  const [isLoading, setIsLoading] = useState(true);
  const [moviesState, setMoviesState] = useState<MoviesState>({
    nowPlaying: [],
    popular: [],
    topRated: [],
    upcoming: [],
  });

  const getMovies = async () => {
    const nowPlayingPromise = movieDB.get<MovieDBMoviesResponse>('/now_playing');
    const popularPromise = movieDB.get<MovieDBMoviesResponse>('/popular');
    const topRatedPromise = movieDB.get<MovieDBMoviesResponse>('/top_rated');
    const upcomingPromise = movieDB.get<MovieDBMoviesResponse>('/upcoming');

    const resps = await Promise.all([
      nowPlayingPromise,
      popularPromise,
      topRatedPromise,
      upcomingPromise,
    ]);
    setMoviesState({
      nowPlaying: resps[0].data.results,
      popular: resps[1].data.results,
      topRated: resps[2].data.results,
      upcoming: resps[3].data.results,
    });

    setIsLoading(false);
  };

  useEffect(() => {
    getMovies();
  }, []);

  return {
    ...moviesState,
    isLoading,
  };
};

```