3

我现在觉得有点傻。我对nodejs和javaScript相当陌生,无法弄清楚这一点。我想这是因为对 mysql 的查询的异步性质......

我做了一个例子来说明我的问题。我只想循环处理一些 sql 查询并对结果进行处理。为了这个例子,我只是打印出一些东西。我知道我可以使用这样的单个 sql 查询,"SELECT id, name FROM player WHERE id IN (1,2,3,4,5)"但这在我尝试编写的实际应用程序中是不可能的。

这是我的 nodejs app.js 的相关部分

var mysql = require("mysql");
var mysqlPool = mysql.createPool(conf.mysqlArbData);

for (var i = 0; i<5; i++){

    mysqlPool.getConnection(function(err, connection) {

        var detailSql = "SELECT id, name FROM player " +
            "WHERE id = "+i;
        if (err){
            throw err;
        }
        connection.query(detailSql, function(err, detailRows, fields) {
            connection.end();
            console.log("detailSql="+detailSql);
            if (err){
                console.log("can't run query=" + detailSql +"\n Error="+err);
            }
            else{

                console.log(detailRows[0].id + " " +detailRows[0].name);

            }

        });
    });

};

现在输出:

web server listening on port 3000 in development mode
detailSql=SELECT id, name FROM player WHERE id = 5
5 Jyvaskyla
detailSql=SELECT id, name FROM player WHERE id = 5
5 Jyvaskyla
detailSql=SELECT id, name FROM player WHERE id = 5
5 Jyvaskyla
detailSql=SELECT id, name FROM player WHERE id = 5
5 Jyvaskyla
detailSql=SELECT id, name FROM player WHERE id = 5
5 Jyvaskyla

我的问题是,为什么我只得到 id=5 的数据库条目的结果?为了在回调中接收每个单独的结果,需要更改什么?

4

3 回答 3

4

问题是它getConnection是异步的,并且Javascript 没有块作用域,这意味着在调用回调时getConnectioni变量将指向它在循环中的最后一个值(即 5)。

您可以使用技巧为循环的每一轮创建一个部分函数(将其视为已应用第一个参数的函数),它将当前值i作为getConnection回调的第一个参数传递:

for (var i = 0; i<5; i++) {
  mysqlPool.getConnection(function(i, err, connection) {
    ...
  }.bind(mysqlPool, i));
};

FWIW,您的代码将几乎立即打开到您的数据库的 5 个连接(并执行 5 个查询)(这就是异步 I/O 的工作方式)。这可能不是一个大问题,但如果它5可以变得更高,这是值得实现的:)

此外,for 循环将生成[0, 1, 2, 3, 4],而在您的示例查询中,您编写WHERE id IN (1, 2, 3, 4, 5).

于 2013-04-17T20:06:27.127 回答
1

对于节点,您可以使用let语句。它限制i了 for 循环的范围。

for (let i = 0; i<5; i++) {
  mysqlPool.getConnection(function(err, connection) {
    console.log(i);
  });
};
于 2016-03-24T10:54:57.870 回答
0

这就是我一直在做的。它适用于包含 50,000 多个项目的UPDATEAND语句。INSERT

const mysql = mysql.createConnection({
  host     :   'HOST',
  user     :   'USER',
  password :   'PASSWORD',
  database :   'DATABASE_NAME',
  port     :   PORT,
  ssl      :   { ca : fs.readFileSync(process.cwd() + '/ssl/certs/ca-certificate.crt') },
});


async function processRequest(itemArray) {

  //pre-processing of array (if needed...)

  // initialize sql connection
  mysql.connect();

  const startProcess = await processItems(itemArray)
    .catch(e => {
      // end sql connection if there was an error running startProcess
      mysql.end();
      throw new Error(`Error @ startProcess: ${e}`);
    })

  // end sql connection after startProcess is finished
  mysql.end();
  
  // optional return of startProcess. This occurs after processItems is done.
  try {
  
    if (startProcess.status == 'success') {
      return {status: 'success', msg: 'function startProcess has compelted.';
    }

  }
  
  catch (e) {
      return {status: 'error', msg: 'function startProcess did not complete.', error: e;
  }


  function processItems(array){

    return new Promise((resolve,reject) => {

      for (let i in array) {

        // perform DB operations inside loop
        mysql.query(`UPDATE db SET ?`, array, (err, results, fields) => {
          // exit if there's any sort of error
          if (err) reject(err)
        })

      }

    // resolve promise after loop completes
    resolve({status: 'success'});

    })

  };

}

这是工作流程:

  1. mysql连接打开(通过mysql.connect()
  2. processRequest被调用(在我的情况下,它是从 a 调用的Route
  3. processItems函数被调用asynchronously
  4. processItems返回一个promise
  5. processItems在它解决或拒绝它的地方完成
  6. mysql 连接已关闭(通过mysql.end()
于 2022-01-30T20:25:50.203 回答