Как использовать обещания и ожидания с функциями обратного вызова Node.js

Большинство API-интерфейсов Node.js были созданы в то время, когда обещаний еще не было, и они использовали решение на основе обратного вызова.

Типичный API Node.js работает так:

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

})

Это также относится к библиотекам. Одним из примеров являетсяnode-redis, и, работая с ним над проектом, в какой-то момент мне действительно пришлось удалить все обратные вызовы, потому что у меня было слишком много уровней обратных вызовов, вложенных друг в друга - идеальный сценарий «ада обратных вызовов».

Кроме того, иногда абсолютно необходимо избегать обратных вызовов, потому что вам нужно вернуть из функции результат вызова функции. Если это возвращается в обратном вызове, единственный способ вернуть результат - это отправить его обратно с помощью функции, и сторона обратного вызова продолжает:

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

Есть простое решение.

Решение, предоставленное самим Node.js.

Мы можем «обещать» любую функцию, которая не поддерживает обещания (и, как следствие, синтаксис async / await), импортировавpromisifyиз ядра Node.jsutilмодуль:

const { promisify } = require('util')

Затем с его помощью мы создаем новые функции:

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

Посмотрите, как я добавилaбуква для обозначенияасинхронный.

Теперь мы можем изменить этот пример «ад обратного вызова»:

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

в гораздо более чистый:

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

Это оптимально при использовании функции, к которой у вас нет доступа, как в этом случае, когда я использую стороннюю библиотеку.

Под капотом promisify оборачивает функцию в обещание и возвращает его.

Вы также можете сделать это вручную, вернув обещание из функции, а затем используя его с помощью 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)

Скачать мою бесплатнуюСправочник по Node.js


Дополнительные руководства по узлам: