Cómo usar promesas y esperar con las funciones basadas en devolución de llamada de Node.js

La mayoría de las API de Node.js se crearon en un momento en el que las promesas aún no existían y utilizan una solución basada en devolución de llamada.

La API típica de Node.js funciona así:

doSomething(param, (err, result) => {

})

Esto también se aplica a las bibliotecas. Un ejemplo esnode-redis, y mientras trabajaba con él en un proyecto, en algún momento realmente tuve la necesidad de eliminar todas las devoluciones de llamada, porque tenía demasiados niveles de devoluciones de llamada anidadas entre sí, un escenario perfecto de "infierno de devolución de llamada".

Además, a veces es absolutamente necesario evitar las devoluciones de llamada porque necesita devolver de la función el resultado de una llamada a la función. Si se devuelve en una devolución de llamada, la única forma de recuperar el resultado sería enviarlo de vuelta con una función, y la parte de devolución de llamada continúa:

const myFunction = () => {
  doSomething(param, (err, result) => {
    return result //can't return this from `myFunction`
  })
}
const myFunction = callback => {
  doSomething(param, (err, result) => {
    callback(result) //no
  })
}

myFunction(result => { console.log(result) })

Hay una solución sencilla.

Una solución proporcionada por el propio Node.js.

Podemos "promisificar" cualquier función que no admita promesas (y, como consecuencia, la sintaxis async / await) importandopromisifydesde el núcleo de Node.jsutilmódulo:

const { promisify } = require('util')

Luego creamos nuevas funciones usándolo:

const ahget = promisify(client.hget).bind(client)
const asmembers = promisify(client.smembers).bind(client)
const ahkeys = promisify(client.hkeys).bind(client)

Mira como agregué elacarta para significarasincrónico.

Ahora podemos cambiar este ejemplo de "infierno de devolución de llamada":

client.hget(`user:${req.session.userid}`, 'username', (err, currentUserName) => {
  client.smembers(`followers:${currentUserName}`, (err, followers) => {
    client.hkeys('users', (err, users) => {
      res.render('dashboard', {
        users: users.filter((user) => user !== currentUserName && followers.indexOf(user) === -1)
      })
    })
  })
})

en un mucho más limpio:

const currentUserName = await ahget(`user:${req.session.userid}`, 'username')
const followers = await asmembers(`followers:${currentUserName}`)    
const users = await ahkeys('users')

res.render(‘dashboard’, { users: users.filter((user) => user !== currentUserName && followers.indexOf(user) === -1) })

Esto es óptimo cuando se usa una función a la que no tiene acceso, como en este caso donde uso una biblioteca de terceros.

Debajo del capó, promisify envuelve la función en una promesa y la devuelve.

También puede hacer esto manualmente, devolviendo una promesa de una función y luego usándola con async / await:

const handleLogin = (req, user) => {
  return new Promise((resolve, reject) => {
    req.login(user, (err) => {
      if (err) {
        return reject({
          error: true,
          message: err,
        })
      }
      return resolve({
        success: true,
      })
    })
  })
}

//… const resultLogin = await handleLogin(req, user)

Descarga mi gratisManual de Node.js


Más tutoriales de nodos: