【问题标题】:NodeJS await/async with nested MySQL query带有嵌套 MySQL 查询的 NodeJS 等待/异步
【发布时间】:2019-05-25 11:39:22
【问题描述】:

我需要有关此代码的帮助:

var sqlCheckIfExist = "SELECT my_refer FROM hub_user WHERE my_refer = '" + friendReferCode + "'";
var sqlCodeCheckSameAsMine = "SELECT my_refer FROM hub_user WHERE uid = '" + uid + "'";

async function checkIfUserCodeExist() {
  connection.promise().query(sqlCheckIfExist)
    .then(([rows, fields]) => {
    if (rows == 0) {
      console.log("Non esiste!")
      return res.send(JSON.stringify({
        "status": 500,
        "response": "codeNotExist"
      }));
    }
    checkIfCodeIsSameAsMine()
    console.log("Esiste!")
    console.log(rows[0].my_refer);
  })
    .catch(console.log)
    .then(() => connection.end());
}

async function checkIfCodeIsSameAsMine() {
  connection.promise().query(sqlCodeCheckSameAsMine)
    .then(([rows, fields]) => {
    if (rows == friendReferCode) {
      console.log("Codice uguale!")
      return res.send(JSON.stringify({
        "status": 500,
        "response": "sameCodeAsMine"
      }));
    }
    console.log("Codice non uguale!")
  })
    .catch(console.log)
    .then(() => connection.end());
}

checkIfUserCodeExist()

我是这样建立连接的:

app.use(function(req, res, next) {
  global.connection = mysql.createConnection({
    host: 'xx',
    user: 'xx',
    password: 'xx',
    database: 'xx'
  });
  connection.connect();
  next();
});

我无法理解一件事: 如何调用嵌套查询?当我检查 rows == 0 到 checkIfUserCodeExist() 函数时,如果它为假,我调用 checkIfCodeIsSameAsMine() 但我收到了这个错误:

Error: Can't add new command when connection is in closed state
at Connection._addCommandClosedState (/usr/myserver/node_modules/mysql2/lib/connection.js:135:17)
at Connection.end (/usr/myserver/node_modules/mysql2/lib/connection.js:836:26)
at connection.promise.query.then.catch.then (/usr/myserver/addReferFriend.js:45:31)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)

我该如何解决这个问题?

我在这里发布完整的文件:

var express = require('express');
var router = express.Router();

/* GET users listing. */
router.post('/', function(req, res, next) {
    var uid = req.body.uid;
    var friendReferCode = req.body.friendReferCode;

    var sqlCheckIfExist = "SELECT my_refer FROM hub_user WHERE my_refer = '" + friendReferCode + "'";
var sqlCodeCheckSameAsMine = "SELECT my_refer FROM hub_user WHERE uid = '" + uid + "'";
async function checkIfUserCodeExist() {
    connection.promise().query(sqlCheckIfExist)
    .then( ([rows,fields]) => {
            if (rows == 0) {
                console.log("Non esiste!")
                return res.send(JSON.stringify({"status": 500,"response": "codeNotExist"}));
            }
            checkIfCodeIsSameAsMine()
            console.log("Esiste!")
            console.log(rows[0].my_refer);
    })
    .catch(console.log)
    .then( () => connection.end());
    }

    async function checkIfCodeIsSameAsMine() {
        connection.promise().query(sqlCodeCheckSameAsMine)
        .then( ([rows,fields]) => {
                if (rows == friendReferCode) {
                    console.log("Codice uguale!")
                    return res.send(JSON.stringify({"status": 500,"response": "sameCodeAsMine"}));
                }
                console.log("Codice non uguale!")
        })
        .catch(console.log)
        .then( () => connection.end());
        }

checkIfUserCodeExist()
});

module.exports = router;

提前致谢!

【问题讨论】:

    标签: mysql node.js asynchronous


    【解决方案1】:

    您的程序中有多个问题需要更新。

    首先,您不能使用全局变量来存储每个请求的数据库连接。如果两个请求同时到达,那么一个请求将覆盖创建的其他请求的connection,因此您可以对两个请求使用相同的连接,和/或您不关闭其中一个连接会导致悬空连接,在最坏的情况下可能会导致您的应用程序无响应。

    要解决该问题,您必须通过请求对象传递连接:

    app.use(async function(req, res, next) {
      try {
        if( req.dbConnection ) {
          // ensure that req.dbConnection was not set already by another middleware
          throw new Error('req.dbConnection was already set')
        }
    
        let connection = mysql.createConnection({
          host: 'xx',
          user: 'xx',
          password: 'xx',
          database: 'xx'
        });
    
        res.on("finish", function() {
          // end the connection after the resonponse was send
          req.dbConnection.end()
        });
    
        // assign a promise base version of connection to request
        req.dbConnection = connection.promise()
    
        // wait for the connection to be established
        await connection.connect();
        next();
      } catch(err) {
        next(err);
      }
    });
    

    要访问每个请求定义的连接,您可以执行以下操作:

    app.get('/', async function(req, res, next) {
       try {
         await checkIfUserCodeExist(req.dbConnection)
    
         // so something here after `checkIfUserCodeExist` finished
       }  catch(err) {
         next(err); // if an error occured pass it to the next
       }
    })
    

    如果你的函数体中没有await,那么async 可以与await 一起使用,那么你不需要在函数之前使用async

    如果函数体中没有await,则需要从函数中返回Promise链,以便调用者可以等待函数完成:

    function checkIfUserCodeExist(connection) {
      return connection.query(sqlCheckIfExist)
        .then(([rows, fields]) => {
          if (rows == 0) {
            console.log("Non esiste!")
    
            return res.send(JSON.stringify({
              "status": 500,
              "response": "codeNotExist"
            }));
          }
          console.log("Esiste!")
          console.log(rows[0].my_refer);
          return  checkIfCodeIsSameAsMine(connection)
        })
    }
    
    function checkIfCodeIsSameAsMine(connection) {
      return connection.query(sqlCodeCheckSameAsMine)
        .then(([rows, fields]) => {
          if (rows == friendReferCode) {
            console.log("Codice uguale!")
            return res.send(JSON.stringify({
              "status": 500,
              "response": "sameCodeAsMine"
            }));
          }
          console.log("Codice non uguale!")
        })
    }
    

    如果你想使用async,它看起来像这样:

    async function checkIfUserCodeExist(connection) {
      let [rows, fields] = await connection.query(sqlCheckIfExist)
    
      if (rows == 0) {
        console.log("Non esiste!")
        return res.send(JSON.stringify({
          "status": 500,
          "response": "codeNotExist"
        }));
      }
    
      await checkIfCodeIsSameAsMine()
    
      console.log("Esiste!")
      console.log(rows[0].my_refer);
    }
    
    async function checkIfCodeIsSameAsMine(connection) {
      let [rows, fields] = await connection.query(sqlCodeCheckSameAsMine)
    
      if (rows == friendReferCode) {
        console.log("Codice uguale!")
        return res.send(JSON.stringify({
          "status": 500,
          "response": "sameCodeAsMine"
        }));
      }
    
      console.log("Codice non uguale!")
    }
    

    你会避免这样的事情:

    return res.send(JSON.stringify({
      "status": 500,
      "response": "codeNotExist"
    }));
    

    您会抛出一个自定义错误,例如:

    throw new CustomError(500, "codeNotExist")
    

    并且有一个错误的中间件:

    app.use(function(err, req, res, next) {
      return res.send({
        "status": err.status,
        "response": err.message
      });
    })
    

    因此,您只有一个地方可以创建错误响应,并且您可以在必要时对该错误响应进行更改,例如添加一些额外的日志记录。

    编辑(以匹配更新的问题)

    /* GET users listing. */
    router.post('/', function(req, res, next) {
      var uid = req.body.uid;
      var friendReferCode = req.body.friendReferCode;
    
      var sqlCheckIfExist = "SELECT my_refer FROM hub_user WHERE my_refer = '" + friendReferCode + "'";
      var sqlCodeCheckSameAsMine = "SELECT my_refer FROM hub_user WHERE uid = '" + uid + "'";
    
      function checkIfUserCodeExist() {
        return req.dbConnection.query(sqlCheckIfExist)
          .then(([rows, fields]) => {
            if (rows == 0) {
              console.log("Non esiste!")
    
              return res.send(JSON.stringify({
                "status": 500,
                "response": "codeNotExist"
              }));
            }
            console.log("Esiste!")
            console.log(rows[0].my_refer);
            return checkIfCodeIsSameAsMine(connection)
          })
      }
    
      function checkIfCodeIsSameAsMine() {
        return req.dbConnection.query(sqlCodeCheckSameAsMine)
          .then(([rows, fields]) => {
            if (rows == friendReferCode) {
              console.log("Codice uguale!")
              return res.send(JSON.stringify({
                "status": 500,
                "response": "sameCodeAsMine"
              }));
            }
            console.log("Codice non uguale!")
          })
      }
    
      checkIfUserCodeExist()
       .catch(next)
    });
    

    【讨论】:

    • 我必须把 app.use(async function(req, res, next) { 放在我使用连接的所有文件中吗?
    • @MicheleZotti 所有中间件都在您启动之前添加到 epxress 应用程序中。对于每个请求,express 应用程序会按照附加的顺序检查请求的中间件中的哪些匹配。所以不,你不会把它放在你所有的文件中,但是你只为数据库连接注册中间件一次并且在你注册任何其他在该连接上中继的中间件之前。跨度>
    • 我编辑了 app.use(async function(req, res, next) { let connection = mysql.createConnection({ ... etc into app.js 如何在其他 js 文件中做调用我作为主要问题发布的所有函数?
    • @MicheleZotti 我更新了代码,因此它包括检查 dbConnection 没有为同一请求设置多次。
    • @MicheleZotti 正如我在答案中所写,连接现在以dbConnection 的形式存储在req 中,并且不能使用req.dbConnection 访问,您需要将其传递给函数你想使用它。或者,对于嵌套函数,您可以将 connection.promise() 替换为 req.dbConnection
    【解决方案2】:

    这可能是因为 您在 checkIfUserCodeExist 函数结束时终止了连接删除以下行,我认为它会起作用:

    connection.end()
    

    或者你想在每次创建方法时打开和关闭它,将返回新连接并在进行任何数据库查询之前调用它。示例:

     function getMysqlConnection() {
         const connection = mysql.createConnection({
             host: 'xx',
             user: 'xx',
             password: 'xx',
             database: 'xx'
         });
         connection.connect();
         return connection;
     }
    
     var sqlCheckIfExist = "SELECT my_refer FROM hub_user WHERE my_refer = '" + friendReferCode + "'";
     var sqlCodeCheckSameAsMine = "SELECT my_refer FROM hub_user WHERE uid = '" + uid + "'";
     async function checkIfUserCodeExist() {
         const connection = getMysqlConnection();
         connection.promise().query(sqlCheckIfExist)
             .then(([rows, fields]) => {
                 if (rows == 0) {
                     console.log("Non esiste!")
                     return res.send(JSON.stringify({ "status": 500, "response": "codeNotExist" }));
                 }
                 checkIfCodeIsSameAsMine()
                 console.log("Esiste!")
                 console.log(rows[0].my_refer);
             })
             .catch(console.log)
             .then(() => connection.end());
     }
    
     async function checkIfCodeIsSameAsMine() {
         const connection = getMysqlConnection();
         connection.promise().query(sqlCodeCheckSameAsMine)
             .then(([rows, fields]) => {
                 if (rows == friendReferCode) {
                     console.log("Codice uguale!")
                     return res.send(JSON.stringify({ "status": 500, "response": "sameCodeAsMine" }));
                 }
                 console.log("Codice non uguale!")
             })
             .catch(console.log)
             .then(() => connection.end());
     }
    
     checkIfUserCodeExist()
    

    【讨论】:

    • 什么时候必须关闭连接?
    • 当您的应用程序关闭时。或者如果您想每次都关闭它,请不要忘记在每次数据库查询之前再次打开它
    • 我每次都在做 connection.end 因为我只需要 1 个响应并停止而不是连续连接。在我的情况下它没有关闭连接并且它试图打开另一个连接并且我收到了我粘贴的错误。我该如何解决?
    • 是的,根本不要使用 global,这是一种不好的做法
    • 好的,然后我直接将连接创建到文件而不是 app.js 中,对吧?
    【解决方案3】:

    好的,您的代码存在多个问题。我将从解决您的具体问题开始,然后提供一些额外的提示。 :)

    你的问题在于这个逻辑:

    connection.promise().query(sqlCheckIfExist)
        .then(([rows, fields]) => {
        // some code 
    
        checkIfCodeIsSameAsMine()
    
       // some code
      })
        .catch(console.log)
        .then(() => connection.end());
    

    checkIfCodeIsSameAsMine() 函数是异步的。因此,在此代码链中发生的情况是您调用 checkIfCodeIsSameAsMine() 但您不等待其结果并立即跳转到关闭数据库连接的最后一个then()。所以,本质上,checkIfCodeIsSameAsMine() 中执行的代码是在您关闭连接之后执行的

    你应该return checkIfCodeIsSameAsMine()。这样,您将等待来自该函数的Promise 响应。

    现在我要补充一点。

    首先,"SELECT my_refer FROM hub_user WHERE uid = '" + uid + "'"; 不好。您将应用程序暴露在 SQL 注入等漏洞中。您应该通过一些解析功能转义 SQL 查询中的动态值。这通常通过 ORM(您使用的 connection())完成。

    其次,如果您使用async 函数,则使用相应的await 函数。像这样:

    async function checkIfUserCodeExist() {
      let rows, fields;
    
      try {
        [rows, fields] = await connection.promise().query(sqlCheckIfExist);
      } catch (err) {
        console.log(err);
      }
      if (rows == 0) {
        console.log("Non esiste!");
        return res.send(JSON.stringify({
          "status": 500,
          "response": "codeNotExist"
        }));
      }
      console.log("Esiste!");
      console.log(rows[0].my_refer);
    
      let result;
      try {
        result = await checkIfCodeIsSameAsMine();
      } catch (err) {
        console.log(err);
      }
    
      // do something with "result" if you wish
    
      await connection.end();
    }
    

    【讨论】:

      猜你喜欢
      • 2020-06-19
      • 2018-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-07
      • 1970-01-01
      • 1970-01-01
      • 2019-02-08
      相关资源
      最近更新 更多