|

Domina las Promesas: async/await en JavaScript

Una Promise en JavaScript es un objeto que representa un valor que puede no estar disponible ahora, pero lo estará en el futuro. Cuando escribís código asíncrono, JavaScript no espera a que la operación termine (como una consulta a la base de datos o un fetch a una API) — devuelve inmediatamente una Promise y tu código sigue ejecutándose. Eso es exactamente por qué no obtienes el valor directamente: porque la operación toma tiempo real, y JavaScript no puede pausar el programa entero esperando.

En 30 segundos

  • Una Promise es un placeholder: promete un valor futuro, pero no es el valor aún
  • Las Promises tienen tres estados: pending (pendiente), fulfilled (resuelta) o rejected (rechazada)
  • Async/await es azúcar sintáctico que oculta Promises, pero por debajo sigue siendo una Promise
  • Await pausa solo la función, no el programa entero — otros código en paralelo sigue corriendo
  • Podés encadenar Promises con .then() o usar async/await para que el código se lea como síncrono

Qué es código síncrono en JavaScript

Cuando escribís JavaScript normal (sin async), el código corre de arriba para abajo. Línea por línea, en orden.

Ponele que escribís:

const nombre = "Juan";
const apellido = "Pérez";
console.log(nombre + " " + apellido);

JavaScript corre la primera línea, luego la segunda, luego la tercera. Punto. Las variables existen cuando las necesitás.

Eso es ejecución síncrona: bloqueante, secuencial, predecible. El programa espera que termine cada línea antes de pasar a la siguiente.

Por qué existe el código asíncrono

Ahora imaginá que tenés que traer datos de un servidor. Una fetch() a una API. Una lectura de archivo. Una consulta a la base de datos.

Esas operaciones toman tiempo real. Ponele 200 milisegundos. Si JavaScript esperara síncronamente (como hace con nombre + " " + apellido), la pantalla se congela. El usuario hace click en un botón y… nada. La pestaña se queda helada esperando que vuelva el servidor. Ojo con esto: es el principal motivo por el cual JavaScript necesita asincronía.

Así que existe el código asíncrono: para que mientras esperas la respuesta del servidor, JavaScript siga haciendo otras cosas. Que actualice la UI, que procese eventos, que corra tu próxima línea de código sin bloquearse esperando. Sobre eso hablamos en ejecutar tareas sin bloquear el código.

El problema es: ¿cómo obtienes el resultado después? Ahí aparecen las Promises.

Qué es una Promise: el placeholder del futuro

Una Promise es esencialmente un proxy de un valor que no tenés todavía. Un placeholder. Un IOU (te debo uno).

Cuando hacés:

const miPromise = fetch('https://api.ejemplo.com/usuarios');

JavaScript devuelve inmediatamente un objeto Promise. No espera a que vuelva el servidor. Devuelve el Promise al toque, y luego, cuando el servidor responde (200ms después, una hora después, lo que sea), el Promise se actualiza con el resultado.

Es como hacer un pedido en un restaurante. Vos pedís, y el mozo te da un número. Ese número es tu Promise. No es la comida aún. Promete que la comida llegará, pero no la tenés en las manos todavía. Mientras la cocinan, vos te podés ir a charlar con un amigo, mirar el menú de nuevo, lo que quieras. El número sigue siendo un número. La comida llega cuando llega, y el número te avisa cuando está lista (si gritamos tu nombre, equivale a que la Promise se resolvió).

Los tres estados de una Promise

Según MDN, una Promise transita por tres estados:

  • Pending (pendiente): la Promise acaba de crearse, la operación está en marcha, todavía no hay respuesta
  • Fulfilled (resuelta): la operación terminó bien, y la Promise ahora tiene un valor
  • Rejected (rechazada): algo salió mal (error de red, timeout, excepción), y la Promise tiene un error

Una Promise solo transita una vez. No puede pasar de fulfilled a rejected. No puede volver a pending. Cuando se resuelve o rechaza, se queda en ese estado para siempre.

Por qué no obtienes el valor inmediatamente

Este es el punto clave del artículo. La razón por la cual no obtuviste el valor al toque es porque la operación toma tiempo real.

Ponele que hacés:

const respuesta = fetch('https://api.ejemplo.com/usuarios');
console.log(respuesta);

Si lo corrés, verás algo como: Promise { <pending> }.

¿Por qué? Porque en ese momento exacto, JavaScript corrió el fetch, obtuvo un Promise (al toque), y antes de que la red devuelva la respuesta, ya imprimió el console.log. La Promise todavía está pending. El valor no está listo.

Si esperas 200 milisegundos y preguntás de nuevo (dentro de un .then(), o con await), vas a ver la respuesta resuelta, es decir, fulfilled. Más contexto en privacidad en tus proyectos de código.

fetch('https://api.ejemplo.com/usuarios')
 .then(respuesta => console.log(respuesta)); // Acá sí ves la respuesta

¿Qué es .then()? Es la forma de decirle a JavaScript: “cuando la Promise se resuelva, ejecutá esta función con el valor adentro”.

Async/await: la forma moderna de las Promises

Async/await es azúcar sintáctico para trabajar con Promises de forma más legible. Pero internamente, sigue siendo Promises.

Cuando escribís una función con async:

async function traerUsuarios() {
 const respuesta = await fetch('https://api.ejemplo.com/usuarios');
 return respuesta;
}

Dos cosas importantes:

Una función async SIEMPRE devuelve una Promise. Aunque en el código escribas return respuesta, JavaScript la envuelve en una Promise automáticamente. Entonces si hacés traerUsuarios(), obtienes una Promise, no el valor directo.

await pausa la función, no el programa. Cuando JavaScript ejecuta await fetch(...), pausa la ejecución de traerUsuarios() esperando a que el fetch termine. Pero el resto del programa sigue corriendo. Si tenés otros eventos, otros timers, otros fetch en paralelo, siguen adelante.

Comparalo con .then():

  • Con .then(): encadenás funciones que corren cuando la Promise se resuelve
  • Con async/await: escribís como si fuera síncrono, pero por debajo sigue siendo asíncrono

Manejo de errores con try/catch

Si el fetch falla (no hay internet, el servidor devuelve 500, timeout), necesitás capturar el error.

Con async/await usás try/catch:

async function traerUsuarios() {
 try {
 const respuesta = await fetch('https://api.ejemplo.com/usuarios');
 const datos = await respuesta.json();
 return datos;
 } catch (error) {
 console.log('Error:', error.message);
 }
}

Si algo falla dentro del try, JavaScript salta al catch. Si no tenés try/catch y falla el fetch, la Promise se rechaza, y si no tenés un .catch() attached, tu aplicación posiblemente lance un “unhandled promise rejection” (error no manejado).

Con .then(), es parecido pero más verboso:

fetch('https://api.ejemplo.com/usuarios')
 .then(respuesta => respuesta.json())
 .then(datos => console.log(datos))
 .catch(error => console.log('Error:', error.message));

Tabla comparativa: callbacks vs .then() vs async/await

EnfoqueSintaxisVentajasDesventajas
CallbacksPass a function as argumentSimple para operaciones únicasCallback hell con múltiples niveles, difícil de leer
.then()Cadena de .then().then().then()Evita callback hell, legible con pocos nivelesSigue siendo encadenado, puede volverse difícil
async/awaitParece síncrono, con async y awaitCódigo muy legible, se parece al síncrono, fácil manejo de errores con try/catchNecesita entender que sigue siendo asíncrono por debajo
promesa javascript async await diagrama explicativo

Errores comunes

1. Pensar que await bloquea el programa entero

Muchos desarrolladores piensan: “Si uso await, todo el programa se detiene hasta que la Promise se resuelva”. Falso. Solo la función async se pausa. El resto del código sigue corriendo. Para más detalles técnicos, mirá herramientas esenciales para desarrollo.

Si tenés dos funciones async corriendo en paralelo, no se esperan mutuamente. Se ejecutan en paralelo.

Corrección: await solo pausa la función en la que está, no el programa.

2. No manejar rechazos de Promise

Escribís un fetch sin try/catch y sin .catch(), la Promise se rechaza, y JavaScript lanza un error “unhandled promise rejection”. Tu aplicación posiblemente falla sin que te des cuenta.

Corrección: Siempre envolvé en try/catch (con async/await) o attach un .catch() (con .then()).

3. Confundir la Promise con el valor resuelto

Hacés:

const datos = fetch('url');
console.log(datos.nombre); // undefined

Porque datos es una Promise, no el JSON. El JSON está dentro de la Promise, en algún momento del futuro. Necesitás .then() o await para extraerlo.

Corrección: Los valores no salen solos de una Promise. Necesitás esperar explícitamente con await o .then(). Te puede servir nuestra cobertura de plataformas para versionar tu código.

Preguntas Frecuentes

¿Qué es una Promise en JavaScript y para qué sirve?

Una Promise es un objeto que representa un valor que existe en el futuro. Sirve para manejar operaciones que toman tiempo (fetch, lectura de archivos, consultas a DB) sin bloquear el programa. Devuelve inmediatamente una Promise, y cuando la operación termina, la Promise se resuelve con el valor o se rechaza con un error.

¿Por qué no obtengo el valor inmediatamente de una función asíncrona?

Porque la operación toma tiempo real. JavaScript no puede esperar: devuelve una Promise (un placeholder) al toque, y cuando termina la operación, la Promise se actualiza. Si quieres el valor, necesitás esperar con await o .then().

¿Cuál es la diferencia entre callbacks, promesas y async/await?

Los callbacks pasaban funciones como argumento (fácil para una operación, difícil para varias encadenadas). Las Promises son objetos que representan valores futuros y se pueden encadenar con .then(). Async/await es azúcar sintáctico para escribir código con Promises de forma más legible, como si fuera síncrono.

¿Qué significa que una Promise esté pending, fulfilled o rejected?

Pending significa que la operación está en marcha, aún no terminó. Fulfilled significa que terminó bien y la Promise tiene un valor. Rejected significa que algo falló (error de red, excepción) y la Promise tiene un error. Una Promise transita una sola vez: no puede pasar de fulfilled a rejected.

¿Cómo manejar errores en promesas y async/await?

Con async/await usás try/catch. Con .then(), usás .catch(). En ambos casos, si la Promise se rechaza, el error se captura y podés manejarlo (mostrar un mensaje, reintentar, lo que sea).

Conclusión

Por qué Promise JavaScript async await es el eje central de la programación asíncrona moderna: porque sin entender que una Promise es un placeholder, no un valor, y que async/await es solo una forma más legible de trabajar con Promises, vas a pasarte horas debuggeando código que “debería funcionar” pero devuelve undefined.

La razón por la que no obtienes el valor inmediatamente es simple y física: la operación toma tiempo real. JavaScript no puede esperar. Devuelve una Promise al toque y sigue adelante. Cuando la operación termina, la Promise se actualiza. Vos tenés que estar ahí esperando (con await o .then()) para extraer el valor.

Si querés código asíncrono legible, usá async/await. Si querés evitar errores silenciosos, manejalos con try/catch. Y si alguna vez una función async te devuelve undefined, ahora sabés por qué: olvidaste el await o el .then().

Fuentes

Similar Posts