如何使用Node正确地从MySQL返回结果?

31
在代码中
var stuff_i_want = '';
stuff_i_want = get_info(parm);

还有函数get_info:

get_info(data){
      var sql = "SELECT a from b where info = data"
      connection.query(sql, function(err, results){
            if (err){ 
              throw err;
            }
            console.log(results[0].objid); // good
            stuff_i_want = results[0].objid;  // Scope is larger than function
            console.log(stuff_i_want); // Yep. Value assigned..
    }

在更大的范围内

stuff_i_want = null

关于返回mysql数据并将其分配给变量,我漏掉了什么?

============ 根据Alex的建议进行的新代码

var parent_id = '';
    get_info(data, cb){
          var sql = "SELECT a from b where info = data"
          connection.query(sql, function(err, results){
                if (err){ 
                  throw err;
                }
                return cb(results[0].objid);  // Scope is larger than function
    }

==== 新代码正在使用

 get_data(parent_recording, function(result){ 
    parent_id = result;
    console.log("Parent ID: " + parent_id); // Data is delivered
  });

然而

console.log("Parent ID: " + parent_id);

在函数外部范围,parent_id为null

6个回答

53

你需要理解JavaScript中的异步调用和回调函数,这与C#、PHP等语言不同...

以下是使用你的代码的示例:

function get_info(data, callback){
      
      var sql = "SELECT a from b where info = data";

      connection.query(sql, function(err, results){
            if (err){ 
              throw err;
            }
            console.log(results[0].objid); // good
            stuff_i_want = results[0].objid;  // Scope is larger than function

            return callback(results[0].objid);
    })
}


//usage

var stuff_i_want = '';

 get_info(parm, function(result){
    stuff_i_want = result;

    //rest of your code goes in here
 });

当您调用get_info时,它会调用连接connection.query,该函数需要一个回调函数(就是function(err, results))。

然后将范围传递给此回调函数,以此类推。

欢迎来到JavaScript的回调地狱......

一旦你掌握了这个技巧,就很容易,只需要花点时间来适应从C#这样的语言过来就行了。


我无法从get_info的回调函数中分配给stuff_i_want。有什么想法吗? - Nagusameta

36

我猜你想要在这里返回一个包含结果的 Promise 对象。这样可以处理从数据库检索数据的异步操作:当你获得结果时,使用 Promise 的 resolve 函数来“返回值” / “解决承诺”。

以下是一个例子:

getEmployeeNames = function(){
  return new Promise(function(resolve, reject){
    connection.query(
        "SELECT Name, Surname FROM Employee", 
        function(err, rows){                                                
            if(rows === undefined){
                reject(new Error("Error rows is undefined"));
            }else{
                resolve(rows);
            }
        }
    )}
)}

在调用方面,您可以使用then函数来管理成功状态,使用catch函数来处理失败状态。

以下是使用以上代码的示例:

getEmployeeNames()
.then(function(results){
  render(results)
})
.catch(function(err){
  console.log("Promise rejection error: "+err);
})

此时,您可以为结果设置视图(这些结果确实作为对象数组返回):

render = function(results){ for (var i in results) console.log(results[i].Name) }

编辑 我正在添加一个基本示例,演示如何返回HTML内容与结果,这是Node的更典型的场景。只需使用Promise的then函数设置HTTP响应,并在http://localhost:3001中打开您的浏览器。

require('http').createServer( function(req, res){
if(req.method == 'GET'){
    if(req.url == '/'){
        res.setHeader('Content-type', 'text/html');
        getEmployeeNames()
        .then(function(results){
          html = "<h2>"+results.length+" employees found</h2>"
          html += "<ul>"
          for (var i in results) html += "<li>" + results[i].Name + " " +results[i].Surname + "</li>";
          html += "</ul>"
          res.end(html);
        })
        .catch(function(err){
          console.log("Promise rejection error: "+err);
          res.end("<h1>ERROR</h1>")
        })
    }
}
}).listen(3001)

很好。我已经用另一个脚本完成了这个任务,但是当我尝试从结果的console.log转移到将它们放入数组时,什么也没有发生。如果我不想让它进入控制台,有什么解决方案? - Ken Ingram
@KenIngram,你应该发布你的代码:你想做什么?我发布的render函数将数组作为参数,所以results是你的对象数组。如果你直接打印它,你会得到类似于[ RowDataPacket { Name: 'James' }, RowDataPacket { Name: 'Mary' } ]的东西。你也可以像这样访问数组属性:results.length - gtugnolo
我在 Promises 方面得到了一些帮助。现在我理解了,你的例子完全有意义。 - Ken Ingram
看着这许多月后,我有时间使用 Promises 和 async/await。所以你的解决方案更加深入人心。 - Ken Ingram
考虑到我当时的理解,这个答案对我来说太复杂了,我无法理解其中的逻辑。现在我理解得更好了,在我的问题中有一个更简单的解释方法。 - Ken Ingram

13

五年后,我更好地理解了异步操作。同时,随着ES6中async/await的新语法,我重构了这段特定的代码:

const mysql = require('mysql2') // built-in promise functionality
const DB = process.env.DATABASE
const conn = mysql.createConnection(DB)

async function getInfo(data){
  var sql = "SELECT a from b where info = data"
  const results = await conn.promise().query(sql)
  return results[0]
}

module.exports = {
  getInfo
}

那么,无论我在哪里需要这些数据,我都会将其包装在一个异步函数中,调用代码getInfo(data)并根据需要使用结果.


1
谢谢,访问数据的最简单方法 - Siva

1

如果您发送一个待解决的承诺会更容易,例如:

function get_info(data, promise){

      var sql = "SELECT a from b where info = data";

      connection.query(sql, function(err, results){
            if (err){ 
              throw err;
            }
            console.log(results[0].objid); // good
            stuff_i_want = results[0].objid;  // Scope is larger than function
            promise.resolve(results[0].objid);
    }
}

这样,Node.js 就能保持快速运行,因为在等待 promise 被解析的时候,它可以忙于处理其他任务。

谢谢。我一直在尝试实现Promises。我卡在将返回的Promise转换为可用数据上。我理解这个概念,但如何完成技术实现仍然困扰着我。 - Ken Ingram
1
db.findOne(272).then((user) => { console.log("在位置策略中找到用户", user[0]); return done(null, user[0]); }).catch(error => { return done(err); }); - Epirocks
我一直在看这个。这是一个答案,但它假设我知道如何理解它。我弄清楚了 Promise,但他的片段实际上并没有帮助我。我需要更简单的分解。神经多样性。 - Ken Ingram
1
仅仅两年后,这个解决方案对我来说更有意义了。但是我对函数签名很好奇。你是在说要像这样调用函数:get_info(data, new Promise())吗?我想此时使用async/await可能更合适。 - Ken Ingram
1
你说得对,这种做法确实有点奇怪。get_data 应该直接返回新的 Promise,这样就可以链式调用了。和你一样,随着时间的推移,我也积累了更多的经验。 - Epirocks

1
这是一个情况,我需要向子表中插入新记录并仅根据名称获取父记录键。
这是理解Node异步性质的一个很好的例子。
我需要将影响子记录的所有代码包装在查找父记录ID的调用内部。
我从顺序(PHP,JAVA)角度来处理这个问题,这是完全错误的。

0

我已经努力几周了,一直没有结果,最终找到了使用await/async和promises将任何mysql查询结果赋值给变量的方法。

您不需要理解promises就可以使用它,嗯,我也不知道如何使用promises

我是这样使用我的数据库模型类的:

class DB {
    constructor(db) {
        this.db = db;
    }


    async getUsers() {
        let query = "SELECT * FROM asimov_users";
          return this.doQuery(query)
    }

    async getUserById(array) {
      let query = "SELECT * FROM asimov_users WHERE id = ?";
      return this.doQueryParams(query, array);
    }


    // CORE FUNCTIONS DON'T TOUCH
    async doQuery(queryToDo) {
        let pro = new Promise((resolve,reject) => {
            let query = queryToDo;
            this.db.query(query, function (err, result) {
                if (err) throw err; // GESTION D'ERREURS
                resolve(result);
            });
        })
        return pro.then((val) => {
            return val;
        })
    }
    async doQueryParams(queryToDo, array) {
      let pro = new Promise((resolve,reject) => {
        let query = queryToDo;
        this.db.query(query, array, function (err, result) {
            if (err) throw err; // GESTION D'ERREURS
            resolve(result);
        });
      })
      return pro.then((val) => {
        return val;
      })
    }
}

接下来,您需要通过将mysql提供的connection变量作为参数传递给构造函数来实例化您的类。之后,您只需要在调用类方法之前加上await即可。这样,您就可以链接查询而不必担心作用域。

例如:

connection.connect(function(err) {
    if (err) throw err;
    let DBModel = new DB(connection);

    (async function() {
        let oneUser = await DBModel.getUserById([1]);
        let allUsers = await DBModel.getUsers();

        res.render("index.ejs", {oneUser : oneUser, allUsers : allUsers});
    })();

});

注意:

  • 如果您需要执行另一个查询,只需在您的类中编写一个新方法,并在您的代码中使用await调用它,放在async function中,只需复制/粘贴一个方法并进行修改即可。
  • 该类中有两个“核心函数”,doQuerydoQueryParams,第一个仅接受字符串作为参数,基本上是您的mysql查询。第二个用于查询中的参数,它接受值数组。
  • 值得注意的是,您的方法的返回值始终是对象数组,这意味着如果您执行仅返回一行的查询,则必须执行var[0]。对于多行情况,只需循环即可。

这是一个有趣的实现。看起来足够简单。我最终找到了一个使用PHP和NODE的解决方案,但我喜欢你的方式。现在我更好地理解了async/Promises。 - Ken Ingram
1
如果您想在真实应用程序中查看此内容,请查看此处的两个文件 https://github.com/Oskar-Ardolo/asimov/tree/master/core 在这里,您可以找到项目增长时它如何逐渐变得更加简单。 - Vincent ROUILLARD MELTZ
哦,我喜欢这个 Vincent。它看起来比我做的更优雅。很棒的例子。 - Ken Ingram

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接