简体   繁体   中英

Execute code synchronously in node.js controller

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

EDIT :

Thanks to all answers, i have cleaned my code and went out of all the then/catch block. It lost a lot of weight :p and so much readable. The error management is so much easier this way too

async function create(req, res, next) {

  const formValue = req.body['form'];
  const riddles = [];

  try {
    //get current user who created this Quest
    const currentUser = await User.findOne({_id: req.body.id});

    /*-------------------------------START QUEST CREATION-------------------------------*/
    let quest = await new Quest({
      admin: currentUser,
      hunter: new User,
      launchDate: formValue.launchDate,
      penaltyTime: formValue.penaltyTime,
    });
    /*-------------------------------END QUEST CREATION-------------------------------*/

    /*-------------------------------START RIDDLES CREATION-------------------------------*/
    let riddle;
    //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
    let hunterUser = await User.findOne({email: formValue.hunterEmail});
    if (hunterUser == null) {//if userHunter doesn't exist yet => create it
      hunterUser = await userHelper.createUser(
        formValue.hunterFirstName,
        formValue.hunterLastName,
        formValue.hunterFirstName + formValue.hunterLastName.slice(-1),//TODO add a unique ID
        formValue.hunterEmail,
        Text.generateRandomPassword()
      )
    }
    await questHelper.saveQuest(quest, riddles, hunterUser)
    return res.status(200).json({'msg': 'Quest created'})


    /*-------------------------------END USER MANAGEMENT-------------------------------*/

  } catch (error) {
    error = Text.parseErrorToObject(error);
    return res.status(parseInt(error.code.toString())).json(error);
  }
};

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

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);
}

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() .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM