Я пытаюсь создать страницу входа в систему, на которой я получаю хешированный пароль из mysql db с помощью Sequelize, а затем вызываю bcrypt compare, чтобы расшифровать пароль и сравнить его с вводом входа пользователя для аутентификации.
Однако сравнение bcrypt всегда выполняется медленнее, чем результат возврата, поэтому значение всегда будет "". Я знаю, что это связано с асинхронным поведением, но я не знаю, как правильно написать этот код, чтобы он работал.
authenticate: (req, res) => {
let userDetails = req.query;
User.findOne({
where: {
username: userDetails.username
}
})
.then((user) => {
// How can I make this so, correctPassword() finishes
// and then the authenticated variable will be either false or true?
let authenticated = correctPassword(userDetails.password, user.password);
return authenticated;
})
.then((authenticated) => {
// right now authenticated is "" in client side console.
res.send(authenticated);
})
.catch((error) => {
console.info('there was an error: ', error);
});
}
}
const correctPassword = (enteredPassword, originalPassword) => {
return bcrypt.compare(enteredPassword, originalPassword, (err, res) =>{
return res;
});
}



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Ты почти там. Вы правильно поняли, что correctPassword выполняется асинхронно, хотя написано так, как будто это синхронно.
Во-первых, давайте сделаем correctPassword обещанием, чтобы мы могли использовать async/await или вызвать .then на нем.
const correctPassword = (enteredPassword, originalPassword) => {
return new Promise(resolve => {
bcrypt.compare(enteredPassword, originalPassword, (err, res) =>{
resolve(res)
});
})
}
Далее у вас есть два подхода к обеспечению правильного выполнения операций в коде:
(Рекомендуется) Используйте синтаксис async/await, позволяющий писать синхронно выглядящий код:
authenticate: async (req, res) => {
let userDetails = req.query;
try {
const user = await User.findOne({
where: {
username: userDetails.username
}
});
const authenticated = await correctPassword(userDetails.password, user.password);
res.send(authenticated);
} catch(e) {
res.status(400).send(e)
}
}
Продолжайте использовать обещания:
authenticate: (req, res) => {
let userDetails = req.query;
User.findOne({
where: {
username: userDetails.username
}
}).then(() => {
correctPassword(userDetails.password, user.password)
.then(authenticated => {
res.send(authenticated)
})
.catch(e => {
res.send(e)
})
})
}
Он более лаконичен и понятен и позволяет избежать «ада обратных вызовов» с обещаниями. В примере с "промисами" мы уже вложили одно обещание. Представим, что нам пришлось вызвать внутри этого обещания другое обещание; код станет гораздо менее читабельным. async/await позволяет нам писать код, который синхронно выглядит
Последний пример мне кажется неуместным. Он не устанавливает переменную user. Даже когда он помещен в (then((user)), он все равно будет выдавать Unhandled rejection TypeError: Cannot read property 'password' of null, если username не найден в базе данных.
Вы не можете назначить асинхронную функцию переменной, которая позже будет использоваться кодом синхронизации. Если вы хотите выполнить функцию синхронизации, вы можете использовать await/aync. Но здесь я рекомендую вам также использовать обещание для функции сравнения.
User.findOne({
where: {
username: userDetails.username
}
})
.then((user) => {
return correctPassword(userDetails.password, user.password);
})
.then((authenticated) => {
res.send(authenticated);
})
Bcrypt также поддерживает обещание.
const correctPassword = (enteredPassword, originalPassword) => {
return bcrypt.compare(enteredPassword, originalPassword).then((res) =>{
return res;
});
}
Спасибо. Это сработало, и я последовал вашей рекомендации в использовании синтаксиса async / await. Не могли бы вы вкратце объяснить мне, почему вы рекомендуете этот метод для продолжения использования обещаний?