节点嵌套函数中的异步/等待?

时间:2017-12-02 12:45:08

标签: javascript node.js async-await

我试图让async / await按顺序触发事件,但似乎我错过了一些东西,因为我的console.log标记触发的顺序与我希望的顺序相反。

我想知道是否与我在users.js中使用嵌套函数有关,但尝试了async / await的多种变体,它始终无法按预期工作。

// index.js

var users = require("./users.js"); 

app.post("/getToken", async function(req, res) {  
    if (req.body.email && req.body.password) {
        const email = req.body.email;
        const password = req.body.password;
        const user = await users(email, password) 
        // running this should output console.log("No 1") 
        // from users.js first, but doesn't ?

        console.log('No 2')
        if (user) {
            var payload = {
                id: user.id
            };
            var token = jwt.encode(payload, cfg.jwtSecret);
            res.json({
                token: token
            });
        } else {
            res.sendStatus(401);
        }
    } else {
        res.sendStatus(401);
    }
});

// users.js

module.exports = function(emailAddress, password) {
    db.connect();
    var query = `
        SELECT 
            id,
            email,
            password,
            salt
        FROM 
            users 
        WHERE 
            email = ?`;
    var query_params = [emailAddress];

    db.query(
        query, 
        query_params, 
        function(error, result, fields) {
            console.log('No 1')

            if (error) throw error;
            if ( result.length == 1 ) {
                if ( checkPass(password, result[0].password, result[0].salt ) ) {
                    return { id: result[0].id }

                } else {
                    console.log("login False | Password");
                    return false;
                }

            } else {
                console.log("login False | username");
                return false;
            }
        }
    )
}

2 个答案:

答案 0 :(得分:4)

您的users.js功能无法返回任何内容。您通过query的回调会做,但整体功能不会。由于它永远不会明确返回任何内容,因此调用它的结果是undefined。如果您await undefined,它就像await Promise.resolve(undefined)一样,因此很快就会调用您的解析处理程序。

您希望该函数返回一个在工作完成之前无法解决的承诺。由于它使用的是旧式Node callbck API,因此使用new Promise来创建该承诺是合理的(或者,为该DB获取或创建启用了promise的API)。

我还怀疑你错误地调用了connect,因为通常情况下这是异步操作,但是你将它视为同步。

见评论:

users.js

module.exports = function(emailAddress, password) {
    return new Promise((resolve, reject) => {
        // Use the callback to know when the connection is established
        db.connect(error => {
            if (error) {
                // Connection failed
                reject(error);
                return;
            }
            var query = `
                SELECT 
                    id,
                    email,
                    password,
                    salt
                FROM 
                    users 
                WHERE 
                    email = ?`;
            var query_params = [emailAddress];
            db.query(
                query, 
                query_params, 
                function(error, result, fields) {
                    // Throwing an error here does nothing useful. Instead,
                    // reject the promise.
                    if (error) {
                        reject(error);
                        return;
                    }
                    // Resolve our promise based on what we got
                    if ( result.length == 1 ) {
                        if ( checkPass(password, result[0].password, result[0].salt ) ) {
                            resolve({ id: result[0].id });
                        } else {
                            console.log("login False | Password");
                            resolve(false);
                        }
                    } else {
                        console.log("login False | username");
                        resolve(false);
                    }
                }
            );
        });
    });
}

然后使用它:

app.post("/getToken", async function(req, res) {  
    // You must handle errors, since `post` won't do anything with the return
    // value of this function
    try {
        if (req.body.email && req.body.password) {
            const email = req.body.email;
            const password = req.body.password;
            // Now this waits here, since `users` returns a promise that
            // isn't resolved until the query completes
            const user = await users(email, password) 
            console.log('No 2')
            if (user) {
                var payload = {
                    id: user.id
                };
                var token = jwt.encode(payload, cfg.jwtSecret);
                res.json({
                    token: token
                });
            } else {
                res.sendStatus(401);
            }
        } else {
            res.sendStatus(401);
        }
    } catch (e) {
        res.sendStatus(401);
    }
});

答案 1 :(得分:3)

问题是db.query函数是异步的 - 您提供的是在数据库调用完成时执行的回调函数。您可能需要在Promise中包装整个函数:

module.exports = function(emailAddress, password) {
  return new Promise(function(resolve, reject) {
    db.connect();
      var query = `
          SELECT 
              id,
              email,
              password,
              salt
          FROM 
              users 
          WHERE 
              email = ?`;
      var query_params = [emailAddress];

      db.query(
          query, 
          query_params, 
          function(error, result, fields) {

              if (error) return reject(error)

              if ( result.length == 1 ) {
                  if ( checkPass(password, result[0].password, result[0].salt ) ) {
                      resolve({id: result[0].id})

                  } else {
                      console.log("login False | Password");
                      reject();
                  }

              } else {
                  console.log("login False | username");
                  reject();
              }
          }
      )
  })
}

您可以详细了解Promise API here

编辑:

所以你应该另外让connect同步。这是我为你重构的一段代码。它应该工作得很好。我使用了一些ES6元素使其更具可读性。

const connect = () => new Promise((resolve, reject) => {
    db.connect((err) => {
      if (err) return reject(err);

      resolve();
    })
  })

const makeDbRequest = (emailAddress, password) => new Promise((resolve, reject) => {
    const query = `
      SELECT 
          id,
          email,
          password,
          salt
      FROM 
          users 
      WHERE 
          email = ?`;

    const query_params = [emailAddress];

    db.query(
        query,
        query_params,
        handleDbData(resolve, reject, password),
    );
  })

const handleDbData = (resolve, reject, password) => (error, result, fields) => {
  if (error) return reject(error)

  if ( result.length == 1 ) {
      if ( checkPass(password, result[0].password, result[0].salt ) ) {
          resolve({id: result[0].id})

      } else {
          console.log("login False | Password");
          reject();
      }

  } else {
      console.log("login False | username");
      reject();
  }
}

module.exports = (emailAddress, password) => new Promise((resolve, reject) => {
    connect()
      .then(() => {
        makeDbRequest(emailAddress, password)
          .then(resolve)
          .catch(reject)
      })
      .catch(reject);
  })