Как решить проблему приоритета метода NodeJS

В этом случае сначала работает метод 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 поддерживает модульную структуру, что способствует организации кода на больших проектах.


3
299
1

Ответ:

Решено

Тот единственный факт, что "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)
});