簡體   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