Comment utiliser les promesses et attendre avec les fonctions basées sur le rappel de Node.js

La plupart des API Node.js ont été construites à une époque où les promesses n'étaient pas encore une chose, et elles utilisent une solution basée sur le rappel.

L'API Node.js typique fonctionne comme ceci:

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

})

Cela s'applique également aux bibliothèques. Un exemple estnode-redis, et tout en travaillant avec lui sur un projet, à un moment donné, j'ai vraiment eu le besoin de supprimer tous les rappels, car j'avais trop de niveaux de rappels imbriqués les uns dans les autres - un scénario parfait de «l'enfer des rappels».

De plus, il est parfois absolument nécessaire d'éviter les rappels car vous devez renvoyer à partir de la fonction le résultat d'un appel de fonction. Si cela est retourné dans un rappel, le seul moyen de récupérer le résultat serait de le renvoyer avec une fonction, et la partie de rappel continue:

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) })

Il existe une solution simple.

Une solution fournie par Node.js lui-même.

Nous pouvons «promettre» toute fonction qui ne prend pas en charge les promesses (et par conséquent la syntaxe async / await) en importantpromisifyà partir du noyau Node.jsutilmodule:

const { promisify } = require('util')

Ensuite, nous créons de nouvelles fonctions en l'utilisant:

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

Voyez comment j'ai ajouté lealettre pour signifierasynchrone.

Maintenant, nous pouvons changer cet exemple d '«enfer de rappel»:

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 beaucoup plus propre:

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) })

Ceci est optimal lorsque vous utilisez une fonction à laquelle vous n'avez pas accès, comme dans ce cas où j'utilise une bibliothèque tierce.

Sous le capot, promisify enveloppe la fonction dans une promesse et la renvoie.

Vous pouvez également le faire manuellement, en renvoyant une promesse à partir d'une fonction, puis en l'utilisant avec 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)

Téléchargez mon gratuitManuel de Node.js


Plus de didacticiels sur les nœuds: