Преобразование обратных вызовов в обещания | Кодементор

С промисами (или асинхронными/ожидающими) работать проще, чем с обратными вызовами. Это особенно верно, когда вы работаете в средах на основе Node. К сожалению, большинство Node API написаны с обратными вызовами.

Сегодня я хочу показать вам, как конвертировать обратные вызовы в промисы.

Прежде чем вы прочитаете эту статью, полезно знать, что такое обещать является.

Преобразование обратных вызовов в стиле Node в обещания

Обратные вызовы из Node API имеют тот же шаблон. Они передаются в функции в качестве последнего аргумента. Вот пример с fs.readFile.

const fs = require('fs') 

fs.readFile(filePath, options, callback)

Кроме того, каждый обратный вызов содержит как минимум два аргумента. Первый аргумент должен быть объектом ошибки.

fs.readFile('some-file', (err, data) => {
  if (err) {
    // Handle error 
  } else {
    // Do something with data
  }
})

Если вы столкнетесь с обратным вызовом этого шаблона, вы можете преобразовать его в обещание с помощью Node. util.promisify.

const fs = require('fs')
const util = require('util')

const readFilePromise = util.promisify(fs.readFile)

Как только вы преобразуете обратный вызов в обещание, вы можете использовать его как любое другое обещание.

readFilePromise(filePath, options)
  .then(data => {/* Do something with data */})
  .catch(err => {/* Handle error */}

Время от времени вы можете столкнуться с API, которые не соответствуют формату обратного вызова Node. В этих ситуациях вы не можете использовать util.promisify. Вам нужно написать собственное обещание.

Написание собственного обещания

Чтобы преобразовать обратный вызов в обещание, вам нужно вернуть обещание.

const readFilePromise = () => {
  return new Promise ((resolve, reject) => {
    // ...  
  })
}

Вы запускаете код с обратным вызовом внутри обещания.

const readFilePromise = () => {
  return new Promise((resolve, reject) => {
    fs.readFile(filePath, options, (err, data) => {
      // ...
    })
  })
}

Если есть ошибка, вы отклоняете обещание. Это позволяет пользователям обрабатывать ошибки в catch.

Если ошибок нет, вы разрешаете обещание. Это позволяет пользователям решать, что делать дальше в then.

const readFilePromise = () => {
  return new Promise((resolve, reject) => {
    fs.readFile(filePath, options, (err, data) => {
      if (err) return reject(err)
      resolve(data)
    })
  })
}

Затем вам нужно предоставить аргументы, такие как filePath а также options к коду внутри обещания. Для этого вы можете использовать отдыхать и распространяться операторы.

const readFilePromise = (...args) => {
  return new Promise((resolve, reject) => {
    fs.readFile(...args, (err, data) => {
      if (err) return reject(err)
      resolve(data)
    })
  })
}

Затем вы можете использовать readFilePromise как обещание.

readFilePromise(filePath, options)
  .then(data => {/* Do something with data */})
  .catch(err => {/* Handle error */}

Преобразование обратных вызовов в стиле Node в обещания

Превратить обратный вызов в стиле Node в промис легко, если вы знаете, как создать промис. Вы выполняете те же действия:

  1. Отклонить, если есть ошибка
  2. Решить иначе

Допустим, у вас есть API, который возвращает data в качестве первого аргумента и err в качестве второго аргумента. Вот что вы делаете:

const shootPeasPromise = (...args) => {
  return new Promise((resolve, reject) => {
    // This is a not a Node styled callback. 
    // 1. data is the first argument 
    // 2. err is the second argument
    shootPeas(...args, (data, err) => {
      if (err) return reject(err)
      resolve(data)
    })
  })
}

Обратные вызовы с несколькими аргументами

Допустим, у вас есть обратный вызов с тремя аргументами:

  1. Объект ошибки
  2. Некоторые данные
  3. Еще одна часть данных
growTrees(options, (error, location, size) => {
  // ... 
})

Вы не можете написать это:

// Note: This does not work 
const growTreesPromise = (...args) => {
  return new Promise((resolve, reject) => {
    growTrees(...args, (error, location, size) => {
      if (err) return reject(err)
      // You can't send two arguments into resolve
      resolve(location, size)
    })
  })
}

Приведенный выше код не работает, потому что промисы могут возвращать только один аргумент. Если вы хотите вернуть много аргументов, вы можете использовать либо массив, либо объект.

// Using an array object
resolve([location, size])

// Using an object
resolve({location, size})

Затем вы можете деструктурировать массив или объект в then вызов.

// If you use arrays
growTreesPromise(options)
  .then([location, size]) => {/* Do something */})

// If you use objects
growTreesPromise(options)
  .then({location, size}) => {/* Do something */})

Спасибо за чтение. Эта статья изначально была размещена на мой блог. Подписаться на моя рассылка если вы хотите больше статей, которые помогут вам стать лучшим разработчиком внешнего интерфейса.

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *