Автор Паскаль Акунне✏️
В приложениях Node.js нередко встречается большое количество вложенных функций обратного вызова, используемых для выполнения нескольких действий. Это обычно называют адом обратных вызовов, поскольку это может сделать код чрезвычайно сложным и неорганизованным.
К счастью, существует решение JavaScript под названием promises, которое решает проблему ада обратных вызовов. В этой статье будет представлен обзор обещаний JavaScript и продемонстрировано, как использовать обещания в Node.js с помощью функции promisfy()
.
В этой статье мы рассмотрим следующее:
- Что такое обещание?
- Как работают обещания?
- Создание пользовательского обещания
- Потребление обещания
- Цепочка обещаний
- Метод Node.js promisfy()
Предварительные условия
Для того чтобы следовать этому примеру, у вас должно быть следующее:
- npm и установленный Node.js
- базовое понимание JavaScript
- установленный VS Code или IDE по вашему выбору.
Что такое обещание?
Обещание — это, по сути, усовершенствование обратных вызовов, которые управляют всеми асинхронными действиями с данными. Обещание в JavaScript представляет собой действие, которое будет либо выполнено, либо отклонено. Если обещание выполнено, оно разрешается; в противном случае оно отклоняется. Обещания, в отличие от обычных обратных вызовов, можно объединять в цепочки.
Как работают обещания?
Обещания JavaScript имеют три состояния: ожидающее, разрешенное и отклоненное.
Состояние ожидания — это начальное состояние, которое возникает при вызове обещания. Пока обещание находится в состоянии ожидания, вызывающая функция продолжает выполняться до тех пор, пока обещание не будет выполнено, возвращая вызывающей функции все данные, которые были запрошены.
Когда обещание завершено, оно переходит либо в состояние resolved, либо в состояние rejected. Состояние resolved указывает на то, что обещание было успешным и что требуемые данные переданы методу .then()
.
Состояние rejected указывает на то, что обещание было отклонено, и ошибка передается методу .catch()
.
Создание пользовательского обещания
Обещания обычно создаются путем вызова конструктора Promise
, который принимает в качестве аргумента одну функцию обратного вызова. Функция обратного вызова, также известная как функция-исполнитель, выполняется сразу после создания обещания.
Функция-исполнитель принимает в качестве аргументов две функции обратного вызова, resolve
и reject
, которые называются ссылками на функции. Функции resolve()
и reject()
принимают по одному аргументу, который может быть строкой, целым числом, булевым числом, объектом или массивом.
Чтобы лучше понять, как создать пользовательское обещание, давайте рассмотрим файл script.js
:
function getSumNum(a, b) {
const customPromise = new Promise((resolve, reject) => {
const sum = a + b;
if(sum <= 5){
resolve("Let's go!!")
} else {
reject(new Error('Oops!.. Number must be less than 5'))
}
})
return customPromise
}
Здесь мы определяем функцию getSumNum()
для вычисления суммы двух целых чисел, a
и b
. Внутри функции мы используем конструктор обещания, new Promise()
, чтобы создать новое обещание.
Затем мы вычисляем sum
из a
и b
. Обратный вызов resolve
выполняется, если sum
меньше или равно 5
. В противном случае вызывается обратный вызов reject
.
Новое обещание передается в переменную customPromise
, которая затем возвращается. В приведенном выше примере мы возвращаем строку, но это также может быть объект или массив.
Теперь, когда мы поняли, как создается обещание, давайте рассмотрим, как оно потребляется.
Потребление обещания
При разработке приложений гораздо чаще приходится использовать обещания, чем создавать их.
Например, когда мы запрашиваем данные с сервера через API, который возвращает обещание, мы используем методы then()
и catch()
, чтобы получить все полученные данные.
promise.then(data => {
console.log(data)
})
.catch(err => {
console.log(err)
})
В приведенном выше коде метод then()
выполняется, когда обещание выполнено обратным вызовом resolve()
. Обратный вызов catch()
вызывается в случае неудачи обещания, передавая ошибку reject()
.
Теперь давайте используем обещание, которое мы создали ранее:
function getSumNum(a, b) {
const customPromise = new Promise((resolve, reject) => {
const sum = a + b;
if(sum <= 5){
resolve("Let's go!!")
} else {
reject(new Error('Oops!.. Number must be less than 5'))
}
})
return customPromise
}
// consuming the promise
getSumNum(1, 3).then(data => {
console.log(data)
})
.catch(err => {
console.log(err)
})
Сумма одного и трех меньше пяти, поэтому выполняется обратный вызов resolve()
. Это, в свою очередь, приводит к выполнению метода then()
. Если мы изменим параметры так, что сумма окажется больше пяти, будет запущен обратный вызов reject()
, и с помощью метода catch()
будет выдана ошибка.
Теперь выполним следующую команду, а затем проверим консоль:
node script.js
Цепочка обещаний
Обещания можно использовать для последовательного выполнения серии асинхронных задач. Цепочка из нескольких методов then()
к одному результату Promise
помогает избежать необходимости кодирования сложных вложенных функций (что может привести к аду обратных вызовов).
Чтобы продемонстрировать цепочку обещаний, давайте воспользуемся предыдущим кодом с некоторыми изменениями:
let value;
function getSumNum(a, b) {
const customPromise = new Promise((resolve, reject) => {
const sum = a + b;
if(sum < 5){
resolve(sum)
} else {
reject(new Error('Oops!.. Number must be less than 5'))
}
})
return customPromise
}
getSumNum(1, 3)
.then(data => {
console.log("initial data: " + data)
value = data + 1 // modifying the returned data
return value
})
.then(newData => {
console.log("modified data: " + newData)
})
.catch(err => {
console.log(err)
})
Здесь мы видим, что результат передается через цепочку методов then()
. Мы начинаем с объявления пустой переменной value
. На этот раз вместо разрешения строки мы передаем значение sum
.
Когда начальный объект promise разрешается, вызывается функция then()
для записи исходных данных в консоль, после чего данные изменяются путем добавления 1
и присвоения полученной суммы переменной value
. Переменная value
передается в следующий метод then()
, где данные записываются в консоль.
Теперь выполним следующую команду:
node script.js
Вот результат:
initial data: 4
modified data: 5
Метод Node.js promisfy()
Промисификация относится к преобразованию. Это преобразование функции, принимающей обратный вызов, в функцию, возвращающую обещание. Промисификация помогает работать с API, основанными на обратных вызовах, сохраняя при этом согласованность кода.
В Node.js есть встроенный модуль util.promisify()
, который позволяет создавать гибкие функции промисификации в JavaScript. util.promisify()
принимает один параметр функции, который содержит функцию, основанную на обратном вызове.
Давайте рассмотрим пример, чтобы лучше понять, как создать функцию промисификации в Node.js.
Сначала мы создадим два файла, promisify.js
и promise.txt
.
В файле promise.txt
мы добавляем следующий текст:
Promisification относится к преобразованию. Это преобразование функции, принимающей обратный вызов, в функцию, возвращающую обещание. Промисификация помогает работать с API, основанными на обратных вызовах, сохраняя согласованность кода.
Далее мы добавляем следующий код в файл promisify.js
:
// Importing the fs module
const fs = require('fs');
// Importing util module
const util = require('util');
// Use promisify to fs.readFile to promise based method
const readFile = util.promisify(fs.readFile);
readFile('./promise.txt', 'utf8') // Reading the .txt file
.then((text) => {
console.log(text);
})
// Log error if any
.catch((err) => {
console.log('Error', err);
});
Для чтения файлов в приведенном выше примере мы используем модуль fs
. Затем мы используем технику util.promisify()
для преобразования fs.readFile
в функцию, основанную на обещании. Вместо обратного вызова приведенный выше метод теперь возвращает обещание.
Теперь выполним следующую команду: node promisify.js
.
Мы видим, что текст из файла promise.txt
записывается в консоль:
Заключение
При разработке приложений Node.js важно понимать, как оптимально использовать обещания. По сравнению с обычной функцией обратного вызова, обещания обеспечивают более четкий, гибкий и организованный способ управления асинхронными операциями.
В Node.js мы можем использовать модуль util.promisify()
, чтобы легко преобразовать стандартную функцию, получающую обратный вызов, в функцию, возвращающую обещание.
Всего 200 ✔️ Мониторинг неудачных и медленных сетевых запросов в производстве
Развертывание веб-приложения или веб-сайта на базе Node — это самое простое. Убедиться, что ваш экземпляр Node продолжает обслуживать ресурсы вашего приложения — вот где все становится сложнее. Если вы заинтересованы в обеспечении успешного выполнения запросов к бэкенду или сторонним сервисам, попробуйте LogRocket.
LogRocket — это как видеорегистратор для веб- и мобильных приложений, записывающий буквально все, что происходит, пока пользователь взаимодействует с вашим приложением. Вместо того чтобы гадать, почему возникают проблемы, вы можете агрегировать и сообщать о проблемных сетевых запросах, чтобы быстро понять первопричину.