DEV Community

Cover image for Promesas en Javascript para React y TypeScript
Kevin Gracia Orejuela
Kevin Gracia Orejuela

Posted on • Updated on

Promesas en Javascript para React y TypeScript

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

const promiseExample = new Promise( (resolve, reject) => {

     if (condition) {
       resolve("data ...");
    } else {
       reject( "error");
    }

});
Enter fullscreen mode Exit fullscreen mode

Consumo

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

Enter fullscreen mode Exit fullscreen mode

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

const autos = {

   buggati: {
     nombre: 'Veyron'
     marca: 'Buggati'
   },

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

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

}
Enter fullscreen mode Exit fullscreen mode
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}`);
     }

});


}
Enter fullscreen mode Exit fullscreen mode

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

const autoId1= 'buggati';

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


Enter fullscreen mode Exit fullscreen mode

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

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

Enter fullscreen mode Exit fullscreen mode

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

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


});

export default movieDB
Enter fullscreen mode Exit fullscreen mode

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

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

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

Enter fullscreen mode Exit fullscreen mode

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

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

Enter fullscreen mode Exit fullscreen mode

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

  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);
  };
Enter fullscreen mode Exit fullscreen mode

Llamamos la función getMovies con ayuda del hook useEffect

  useEffect(() => {
    getMovies();
  }, []);
Enter fullscreen mode Exit fullscreen mode

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

  return {
    ...moviesState,
    isLoading,
  };
Enter fullscreen mode Exit fullscreen mode

Código completo

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,
  };
};

Enter fullscreen mode Exit fullscreen mode

Top comments (0)