Comprender las promesas de JavaScript

Las promesas son una forma de lidiar con el código asincrónico en JavaScript, sin escribir demasiadas devoluciones de llamada en su código.

Introducción a las promesas

Una promesa se define comúnmente comoun proxy de un valor que eventualmente estará disponible.

Las promesas son una forma de lidiar con el código asincrónico, sin escribir demasiadas devoluciones de llamada en su código.

Aunque han existido durante años, se estandarizaron y se introdujeron enES2015, y ahora han sido reemplazados enES2017porfunciones asincrónicas.

Funciones asincrónicasuse la API de promesas como su bloque de construcción, por lo que comprenderlas es fundamental incluso si en el código más nuevo probablemente use funciones asíncronas en lugar de promesas.

Cómo funcionan las promesas, en resumen

Una vez que se ha llamado a una promesa, comenzará enestado pendiente. Esto significa que la función de llamada continúa con la ejecución, mientras espera la promesa de hacer su propio procesamiento, y le da a la función de llamada alguna retroalimentación.

En este punto, la función de llamada espera a que devuelva la promesa en unestado resuelto, o en unestado rechazado, perola función continúa su ejecución mientras la promesa lo hace.

¿Qué promesas de uso de JS API?

Además de su propio código y código de biblioteca, las API web modernas estándar utilizan promesas como:

Es poco probable que en JavaScript moderno te encuentresnousando promesas, así que comencemos a sumergirnos en ellas.


Creando una promesa

La API de Promise expone un constructor de Promise, que inicializa usandonew Promise():

let done = true

const isItDoneYet = new Promise((resolve, reject) => { if (done) { const workDone = ‘Here is the thing I built’ resolve(workDone) } else { const why = ‘Still working on something else’ reject(why) } })

Como puede ver, la promesa comprueba eldonevariable global, y si eso es cierto, devolvemos una promesa resuelta; de lo contrario, una promesa rechazada.

Usandoresolveyrejectpodemos comunicar un valor, en el caso anterior solo devolvemos una cadena, pero también podría ser un objeto.


Consumiendo una promesa

En la última sección, presentamos cómo se crea una promesa.

Ahora veamos cómo puede ser la promesaconsumadoo usado.

const isItDoneYet = new Promise()
//...

const checkIfItsDone = () => {
  isItDoneYet
    .then(ok => {
      console.log(ok)
    })
    .catch(err => {
      console.error(err)
    })
}

CorriendocheckIfItsDone()ejecutará elisItDoneYet()promesa y esperará a que se resuelva, utilizando elthendevolución de llamada, y si hay un error, lo manejará en elcatchllamar de vuelta.


Encadenando promesas

Una promesa se puede devolver a otra promesa, creando una cadena de promesas.

Un gran ejemplo de encadenamiento de promesas lo da elObtener API, una capa sobre la API XMLHttpRequest, que podemos usar para obtener un recurso y poner en cola una cadena de promesas para ejecutar cuando se recupere el recurso.

La API Fetch es un mecanismo basado en promesas y las llamadasfetch()es equivalente a definir nuestra propia promesa usandonew Promise().

Ejemplo de encadenamiento de promesas

const status = response => {
  if (response.status >= 200 && response.status < 300) {
    return Promise.resolve(response)
  }
  return Promise.reject(new Error(response.statusText))
}

const json = response => response.json()

fetch(’/todos.json’) .then(status) .then(json) .then(data => { console.log(‘Request succeeded with JSON response’, data) }) .catch(error => { console.log(‘Request failed’, error) })

En este ejemplo, llamamosfetch()para obtener una lista de elementos TODO de latodos.jsonarchivo que se encuentra en la raíz del dominio, y creamos una cadena de promesas.

Corriendofetch()devuelve unrespuesta, que tiene muchas propiedades, y dentro de ellas hacemos referencia:

  • status, un valor numérico que representa el código de estado HTTP
  • statusText, un mensaje de estado, que esOKsi la solicitud tuvo éxito

responsetambién tiene unjson()método, que devuelve una promesa que se resolverá con el contenido del cuerpo procesado y transformado enJSON.

Entonces, dadas esas premisas, esto es lo que sucede: la primera promesa en la cadena es una función que definimos, llamadastatus(), que verifica el estado de la respuesta y si no es una respuesta exitosa (entre 200 y 299), rechaza la promesa.

Esta operación hará que la cadena de promesas omita todas las promesas encadenadas enumeradas y pasará directamente a lacatch()declaración en la parte inferior, registrando elRequest failedtexto junto con el mensaje de error.

Si eso tiene éxito en cambio, llama aljson()función que definimos. Dado que la promesa anterior, cuando tuvo éxito, devolvió elresponseobjeto, lo obtenemos como entrada a la segunda promesa.

En este caso, devolvemos los datos JSON procesados, por lo que la tercera promesa recibe el JSON directamente:

.then((data) => {
  console.log('Request succeeded with JSON response', data)
})

y lo registramos en la consola.


Manejo de errores

En el ejemplo anterior, en la sección anterior, teníamos uncatchque se adjuntó a la cadena de promesas.

Cuando algo en la cadena de promesas falla y genera un error o rechaza la promesa, el control va al más cercano.catch()declaración en la cadena.

new Promise((resolve, reject) => {
  throw new Error('Error')
}).catch(err => {
  console.error(err)
})

// or new Promise((resolve, reject) => { reject(‘Error’) }).catch(err => { console.error(err) })

Errores en cascada

Si dentro delcatch()si genera un error, puede agregar un segundocatch()para manejarlo, y así sucesivamente.

new Promise((resolve, reject) => {
  throw new Error('Error')
})
  .catch(err => {
    throw new Error('Error')
  })
  .catch(err => {
    console.error(err)
  })

Orquestando promesas

Promise.all()

Si necesita sincronizar diferentes promesas,Promise.all()le ayuda a definir una lista de promesas y ejecutar algo cuando estén todas resueltas.

Ejemplo:

const f1 = fetch('/something.json')
const f2 = fetch('/something2.json')

Promise.all([f1, f2]) .then(res => { console.log(‘Array of results’, res) }) .catch(err => { console.error(err) })

losAsignación de desestructuración ES2015la sintaxis te permite también hacer

Promise.all([f1, f2]).then(([res1, res2]) => {
  console.log('Results', res1, res2)
})

No estás limitado a usarfetchpor supuesto,cualquier promesa es buena para ir.

Promise.race()

Promise.race()se ejecuta tan pronto como una de las promesas que le pasa se resuelve, y ejecuta la devolución de llamada adjunta solo una vez con el resultado de la primera promesa resuelta.

Ejemplo:

const promiseOne = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 'one')
})
const promiseTwo = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'two')
})

Promise.race([promiseOne, promiseTwo]).then(result => { console.log(result) // ‘two’ })

Errores comunes

Error de tipo no detectado: indefinido no es una promesa

Si obtienes elUncaught TypeError: undefined is not a promiseerror en la consola, asegúrese de usarnew Promise()en lugar de soloPromise()


Más tutoriales de js: