Just started with Node.js few weeks ago, i'm trying to execute this code synchronously located in my controller
Quest.js
async function create(req, res, next) {
const formValue = req.body['form'];
let quest;
const riddles = [];
try {
//get current user who created this Quest
await User.findOne({_id: req.body.id})
.then(currentUser => {
/*-------------------------------START QUEST CREATION-------------------------------*/
quest = new Quest({
admin: currentUser,
hunter: new User,
launchDate: formValue.launchDate,
penaltyTime: formValue.penaltyTime,
});
/*-------------------------------END QUEST CREATION-------------------------------*/
/*-------------------------------START RIDDLES CREATION-------------------------------*/
let riddle;
console.log('step1');
//Riddles instantiation
for (const [key, participantEmail] of Object.entries(formValue.participantsEmail)) {
if (formValue.riddle.text == "") {
throw ('errorMsg:IncorrectRiddleText/code:422/field:riddleText');
}
if (formValue.riddle.answer == "") {
throw ('errorMsg:IncorrectRiddleAnswer/code:422/field:riddleAnswer');
}
//if its the first riddle => i.e : the one created by the current user
if (`${key}` == 0) {
riddle = new Riddle({
quest: quest,
author: currentUser,
authorEmail: currentUser.email,
text: formValue.riddle.text,
answer: formValue.riddle.answer,
status: true,
nextRiddle: null
});
}
//if it's a waiting riddle
else {
if (!validator.validateEmail(participantEmail.participantEmail)) {
throw ('errorMsg:IncorrectEmail/code:422/field:participantEmail');
}
if (participantEmail.participantEmail)
riddle = new Riddle({
quest: quest,
authorEmail: `${participantEmail.participantEmail}`,
nextRiddle: null
});
//assign last created riddle to the one before to make the good order
riddles[`${key}` - 1].nextRiddle = riddle;
}
//create riddle list for Quest
riddles.push(riddle);
}
/*-------------------------------END RIDDLES CREATION-------------------------------*/
/*-------------------------------START USER MANAGEMENT-------------------------------*/
//create a User for the hunter or if he already have an account => link it to the new Quest
User.findOne({email: formValue.hunterEmail})
.then(hunterUser => {
console.log('step2');
if (hunterUser == null) {//if userHunter doesn't exist yet => create it
userHelper.createUser(
formValue.hunterFirstName,
formValue.hunterLastName,
formValue.hunterFirstName formValue.hunterLastName.slice(-1),
formValue.hunterEmail,
Text.generateRandomPassword()
).then((createdUser) => {
console.log('step3');
hunterUser = createdUser;
}).catch(error => {
console.log('error1')
error = Text.parseErrorToObject(error)
return res.status(parseInt(error.code.toString())).json(error);
}
);
}
console.log('step4');
questHelper.saveQuest(quest, riddles, hunterUser)
.then(() => {
console.log('step5');
return res.status(200).json({'msg': 'Quest created'})
})
.catch(error => {
console.log('error2')
error = Text.parseErrorToObject(error)
return res.status(parseInt(error.code.toString())).json(error);
}
);
}
).then().catch(error => {
throw (Text.parseErrorToObject(error));
})
/*-------------------------------END USER MANAGEMENT-------------------------------*/
})
.catch(error => {
throw (Text.parseErrorToObject(error));
}
);
} catch (e) {
console.log('error3')
return res.status(parseInt(e.code.toString())).json(e);
}
};
But when i execute this if i display the logs ihave this :
step1
step2
step4
step3
I know why it's doing this, du to the fact that JS is asynchronous multi-thtreading. But i can't figure out how to execute this block (step4) :
questHelper.saveQuest(quest, riddles, hunterUser)
.then(() => {
console.log('step5');
return res.status(200).json({'msg': 'Quest created'})
})
.catch(error => {
console.log('error2')
error = Text.parseErrorToObject(error)
return res.status(parseInt(error.code.toString())).json(error);
}
);
When the previous block is over. That mean, when hunterUser = createdUser;
is executed
CodePudding user response:
clearly you already understand how "async" and "await" work. Now it's just about remembering to apply them for every network I/O operation.
And so if you change
await User.findOne({_id: req.body.id})
.then(currentUser => {
to
await User.findOne({_id: req.body.id})
.then(async currentUser => {
then suddenly you'll be able to use await
inside that block.
Now you need to change
User.findOne({email: formValue.hunterEmail})
.then(hunterUser => {
to
await User.findOne({email: formValue.hunterEmail})
.then(hunterUser => {
your current solution basically says "find user and once you're done do this and that..." and then you don't actually wait until the user is found, so that basically has to be fixed.
Better yet, you could drop all of the '.then()' and '.catch' blocks since they can be easily replaced with the "async/await" syntax.
So instead of writing
await User.findOne({email: formValue.hunterEmail})
.then(hunterUser => { ...someLogic })
in a more modern way you could write
const hunerUser = await User.findOne({email: formValue.hunterEmail});
//...someLogic
CodePudding user response:
First, you're using await
and .then()
at the same line of code, which won't produce any value.
I'd suggest using the ES6 async
await
keywords alone which will make your parts of code execeute after each promise has resolved.
Step 4 should look something like this:
try {
const result = await questHelper.saveQuest(quest, riddles, hunterUser);
console.log('step5');
return res.status(200).json({'msg': 'Quest created'})
}
catch (error) {
console.log('error2')
error = Text.parseErrorToObject(error)
return res.status(parseInt(error.code.toString())).json(error);
}
CodePudding user response:
Any logic that should happen after the asynchronous operation should be moved to its callback function. Currently you have this structure:
User.findOne({email: formValue.hunterEmail})
.then(hunterUser => {
//...
userHelper.createUser(/*...*/)
.then((createdUser) => {
//...
// YOU WANT TO DO SOMETHING HERE
});
// THIS IS THE CODE YOU WANT DO EXECUTE ABOVE
});
If you want that later block of code to be executed in response to the completion of the userHelper.createUser
operation, move it to the callback where you respond to the result of that operation:
User.findOne({email: formValue.hunterEmail})
.then(hunterUser => {
//...
userHelper.createUser(/*...*/)
.then((createdUser) => {
//...
// THIS IS THE CODE YOU WANT DO EXECUTE
});
});
Overall it seems you're getting confused by a large and complex set of nested callbacks. The async
and await
syntax for Promises
is meant to address that and make the syntax more clear. If these asynchronous operations return Promises
then you're definitely encouraged to make use of that syntax instead of all of these .then()
callbacks.
But even with the .then()
syntax, all you need to do is identify your blocks of code and where they belong. Either something should execute immediately (after the .then()
), or it should execute in response to an asynchronous operation (inside the .then()
.