Преобразование обратных вызовов в обещания | Кодементор
С промисами (или асинхронными/ожидающими) работать проще, чем с обратными вызовами. Это особенно верно, когда вы работаете в средах на основе 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 в промис легко, если вы знаете, как создать промис. Вы выполняете те же действия:
- Отклонить, если есть ошибка
- Решить иначе
Допустим, у вас есть 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)
})
})
}
Обратные вызовы с несколькими аргументами
Допустим, у вас есть обратный вызов с тремя аргументами:
- Объект ошибки
- Некоторые данные
- Еще одна часть данных
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 */})
Спасибо за чтение. Эта статья изначально была размещена на мой блог. Подписаться на моя рассылка если вы хотите больше статей, которые помогут вам стать лучшим разработчиком внешнего интерфейса.