繁体   English   中英

在 node.js 控制器中同步执行代码

[英]Execute code synchronously in node.js controller

几周前刚开始使用 Node.js,我正在尝试在我的控制器中同步执行此代码

任务.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);
      }
    };
    

但是当我执行这个时,如果我显示日志我有这个:

step1
step2
step4
step3

我知道它为什么这样做,因为 JS 是异步多线程。 但我不知道如何执行这个块(第 4 步):

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

当上一个区块结束时。 这意味着,当hunterUser = createdUser; 被执行

编辑 :

感谢所有答案,我已经清理了我的代码并退出了所有 then/catch 块。 它减轻了很多重量:p并且可读性很强。 这种方式的错误管理也容易得多

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

显然,您已经了解“异步”和“等待”是如何工作的。 现在只需记住将它们应用于每个网络 I/O 操作。

所以如果你改变

await User.findOne({_id: req.body.id})
          .then(currentUser => {

await User.findOne({_id: req.body.id})
          .then(async currentUser => {

然后突然之间,您就可以在该块内使用await了。

现在你需要改变

User.findOne({email: formValue.hunterEmail})
              .then(hunterUser => {

await User.findOne({email: formValue.hunterEmail})
              .then(hunterUser => {

您当前的解决方案基本上是说“找到用户,一旦你完成了这个和那个......”然后你实际上并没有等到找到用户,所以基本上必须修复。

更好的是,您可以删除所有 '.then()' 和 '.catch' 块,因为它们可以很容易地替换为“async/await”语法。

所以不要写

await User.findOne({email: formValue.hunterEmail})
              .then(hunterUser => { ...someLogic })

用更现代的方式你可以写

const hunerUser = await User.findOne({email: formValue.hunterEmail});
//...someLogic

首先,您在同一行代码中使用await.then() ,这不会产生任何值。 我建议单独使用 ES6 async await关键字,这将使您的代码部分在每个承诺解决后执行。

第 4 步应如下所示:

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

任何应该在异步操作之后发生的逻辑都应该移到它的回调函数中。 目前你有这个结构:

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

如果您希望稍后执行代码块以响应userHelper.createUser操作的完成,请将其移动到您响应该操作结果的回调:

User.findOne({email: formValue.hunterEmail})
  .then(hunterUser => {
    //...
    userHelper.createUser(/*...*/)
      .then((createdUser) => {
        //...
        // THIS IS THE CODE YOU WANT DO EXECUTE
      });
  });

总体而言,您似乎对大量复杂的嵌套回调感到困惑。 Promisesasyncawait语法旨在解决这个问题并使语法更加清晰。 如果这些异步操作返回Promises ,那么绝对鼓励您使用该语法而不是所有这些.then()回调。

但即使使用.then()语法,您需要做的只是识别代码块及其所属位置。 某些东西应该立即执行(在.then()之后),或者它应该响应异步操作(在.then()内部)执行。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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