В этом случае сначала работает метод 3, и я получаю сообщение об ошибке. Его приоритет должен быть подобен методу 1, методу 2 и методу 3. Являются ли эти методы обещаниями? И обещания работают асинхронно.
Я хочу проверить, используются ли имя пользователя и адрес электронной почты нового пользователя. Если имя пользователя или адрес электронной почты не используются, зарегистрируйте его.
Как решить эту проблему? Я новичок в nodejs.
module.exports.addUser = function(newUser, callback) {
// method 1
User.countDocuments({username: newUser.username}).then(count => {
if (count > 0) {
console.info("username in use");
callback("username in use", null);
return;
}});
// method 2
User.countDocuments({email: newUser.email}).then(count => {
if (count > 0) {
console.info("email in use");
callback("email in use", null);
return;
}});
// method 3 , this method works first
bcrypt.genSalt(10, (err, salt) => {
console.info("salt here");
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) throw err;
newUser.password = hash;
newUser.save(callback);
});
});
};
Выход:
salt here
username in use
email in use
(node:7972) UnhandledPromiseRejectionWarning: Error: Can't set headers after they are sent.
at validateHeader (_http_outgoing.js:491:11)
at ServerResponse.setHeader (_http_outgoing.js:498:3)
at ServerResponse.header (C:\Users\cyclone\Desktop\my_auth\node_modules\express\lib\response.js:767:10)
at ServerResponse.send (C:\Users\cyclone\Desktop\my_auth\node_modules\express\lib\response.js:170:12)
at ServerResponse.json (C:\Users\cyclone\Desktop\my_auth\node_modules\express\lib\response.js:267:15)
at User.addUser (C:\Users\cyclone\Desktop\my_auth\routes\users.js:20:17)
at User.countDocuments.then.count (C:\Users\cyclone\Desktop\my_auth\models\user.js:48:13)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:189:7)
(node:7972) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:7972) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled
will terminate the Node.js process with a non-zero exit code.
events.js:183
throw er; // Unhandled 'error' event
^
Error: Can't set headers after they are sent.
at validateHeader (_http_outgoing.js:491:11)
at ServerResponse.setHeader (_http_outgoing.js:498:3)
at ServerResponse.header (C:\Users\cyclone\Desktop\my_auth\node_modules\express\lib\response.js:767:10)
at ServerResponse.send (C:\Users\cyclone\Desktop\my_auth\node_modules\express\lib\response.js:170:12)
at ServerResponse.json (C:\Users\cyclone\Desktop\my_auth\node_modules\express\lib\response.js:267:15)
at User.addUser (C:\Users\cyclone\Desktop\my_auth\routes\users.js:22:17)
at C:\Users\cyclone\Desktop\my_auth\node_modules\mongoose\lib\model.js:4518:16
at model.$__save.error (C:\Users\cyclone\Desktop\my_auth\node_modules\mongoose\lib\model.js:422:7)
at C:\Users\cyclone\Desktop\my_auth\node_modules\kareem\index.js:315:21
at next (C:\Users\cyclone\Desktop\my_auth\node_modules\kareem\index.js:209:27)
at C:\Users\cyclone\Desktop\my_auth\node_modules\kareem\index.js:182:9
at process.nextTick (C:\Users\cyclone\Desktop\my_auth\node_modules\kareem\index.js:499:38)
at _combinedTickCallback (internal/process/next_tick.js:132:7)
at process._tickCallback (internal/process/next_tick.js:181:9)
[nodemon] app crashed - waiting for file changes before starting...
🤔 А знаете ли вы, что...
JavaScript поддерживает модульную структуру, что способствует организации кода на больших проектах.
Тот единственный факт, что "then" используется после countDocuments
, действительно указывает на то, что это обещание и, следовательно, асинхронность.
Самым простым решением на данном этапе было бы определение вашей функции addUser
как async
.
module.exports.addUser = async function(newUser, callback) {
// method 1
const count1 = await User.countDocuments({
username: newUser.username
});
if (count1 > 0) {
console.info("username in use");
callback("username in use", null);
return;
};
// method 2
const count2 = await User.countDocuments({
email: newUser.email
});
if (count2 > 0) {
console.info("email in use");
callback("email in use", null);
return;
};
// method 3 , this method works first
bcrypt.genSalt(10, (err, salt) => {
console.info("salt here");
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) throw err;
newUser.password = hash;
newUser.save(callback);
});
});
};
Однако сейчас совершенно бессмысленно иметь функцию обратного вызова для addUser, поскольку асинхронная функция автоматически возвращает обещание. Я бы порекомендовал сделать что-то подобное ...
module.exports.addUser = async function(newUser) {
// method 1
const count1 = await User.countDocuments({
username: newUser.username
});
if (count1 > 0) {
throw Error("username is in use");
};
// method 2
const count2 = await User.countDocuments({
email: newUser.email
});
if (count2 > 0) {
throw Error("email in use");
};
let result = null;
// method 3 , this method works first
bcrypt.genSalt(10, (err, salt) => {
console.info("salt here");
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) throw err;
newUser.password = hash;
result = await newUser.save(callback);
});
});
return result;
};
и при использовании это будет выглядеть так:
addUser(someUserObject).then(result=>console.info(result)).catch(error=>{
//Example: username in use
console.info(error.message)
});