【问题标题】:Use promise to process MySQL return value in node.js在node.js中使用promise处理MySQL返回值
【发布时间】:2016-08-01 12:46:37
【问题描述】:

我有 python 背景,目前正在迁移到 node.js。由于它的异步性质,我无法适应 node.js。

例如,我试图从 MySQL 函数返回一个值。

function getLastRecord(name)
{
    var connection = getMySQL_connection();

    var query_str =
    "SELECT name, " +
    "FROM records " +   
    "WHERE (name = ?) " +
    "LIMIT 1 ";

    var query_var = [name];

    var query = connection.query(query_str, query_var, function (err, rows, fields) {
        //if (err) throw err;
        if (err) {
            //throw err;
            console.log(err);
            logger.info(err);
        }
        else {
            //console.log(rows);
            return rows;
        }
    }); //var query = connection.query(query_str, function (err, rows, fields) {
}

var rows = getLastRecord('name_record');

console.log(rows);

经过一番阅读,我意识到上面的代码不能工作,由于 node.js 的异步特性,我需要返回一个 Promise。我不能像 python 那样编写 node.js 代码。如何将getLastRecord() 转换为返回一个promise,如何处理返回值?

其实我想做的就是这样的;

if (getLastRecord() > 20)
{
    console.log("action");
}

如何在 node.js 中以可读的方式做到这一点?

我想看看在这种情况下如何使用 bluebird 实现 Promise。

【问题讨论】:

    标签: mysql node.js asynchronous promise node-mysql


    【解决方案1】:

    你不需要使用promise,你可以使用回调函数,类似这样:

    function getLastRecord(name, next)
    {
        var connection = getMySQL_connection();
    
        var query_str =
        "SELECT name, " +
        "FROM records " +    
        "LIMIT 1 ";
    
        var query_var = [name];
    
        var query = connection.query(query_str, query_var, function (err, rows, fields) {
            //if (err) throw err;
            if (err) {
                //throw err;
                console.log(err);
                logger.info(err);
                next(err);
            }
            else {
                //console.log(rows);
                next(null, rows);
            }
        }); //var query = connection.query(query_str, function (err, rows, fields) {
    }
    
    getLastRecord('name_record', function(err, data) {
       if(err) {
          // handle the error
       } else {
          // handle your data
    
       }
    });
    

    【讨论】:

    • 谢谢。有什么办法可以做类似if (getLastRecord() > 20> 的事情,或者至少让它可读吗?
    • @user16891328 你必须在回调中完成,getLastRecord('name_record', function(err, data) { if(err) {} else { if(data.length > 20) }});
    • 好的。谢谢。好像没有别的选择了。代码比python可读性差。
    • 嗯,问题在于它的异步特性,你必须等待回调。
    • @user16891328 如果您认为您的代码更具可读性,您可以使用 Promise。 getLastRecord('name_record').then(function(data) { if(data.length > 20) { // dosomething } });
    【解决方案2】:

    这里会有点散,见谅。

    首先,假设此代码正确使用了 mysql 驱动程序 API,您可以通过以下方式将其包装为使用原生 Promise:

    function getLastRecord(name)
    {
        return new Promise(function(resolve, reject) {
            // The Promise constructor should catch any errors thrown on
            // this tick. Alternately, try/catch and reject(err) on catch.
            var connection = getMySQL_connection();
    
            var query_str =
            "SELECT name, " +
            "FROM records " +   
            "WHERE (name = ?) " +
            "LIMIT 1 ";
    
            var query_var = [name];
    
            connection.query(query_str, query_var, function (err, rows, fields) {
                // Call reject on error states,
                // call resolve with results
                if (err) {
                    return reject(err);
                }
                resolve(rows);
            });
        });
    }
    
    getLastRecord('name_record').then(function(rows) {
        // now you have your rows, you can see if there are <20 of them
    }).catch((err) => setImmediate(() => { throw err; })); // Throw async to escape the promise chain
    

    所以有一件事:你仍然有回调。回调只是您将其交给某些东西以在未来某个时间调用的函数,并带有其选择的参数。所以xs.map(fn) 中的函数参数、node 中看到的(err, result) 函数以及promise 结果和错误处理程序都是回调。人们将特定类型的回调称为“回调”,这有点混淆,(err, result) 在节点核心中以所谓的“持续传递样式”使用,有时被不真正了解的人称为“nodebacks”喜欢他们。

    至少就目前而言(async/await 最终会出现),无论您是否采用 Promise,您几乎都被回调所困扰。

    另外,我会注意到,promise 不是立即的,在这里显然很有帮助,因为你仍然有一个回调。只有当您将 Promise 与 Promise.all 结合使用并承诺累加器 a la Array.prototype.reduce 时,Promise 才会真正发挥作用。但它们确实有时会发光,而且它们值得学习。

    【讨论】:

    • 哦,如果您确实使用了 Promise,请考虑使用 bluebird!它有许多不错的助手、良好的易于理解的性能等。
    • 如果我使用bluebird,我可以使用我的getLastRecord() 函数并执行Promisify(getLastRecord)getLastRecord() 之类的操作支持promise 吗?
    • 顺便说一句:try..catch 块可以避免,因为在new Promise 函数中,throw 将自动调用reject(err),如果不是catched。 (至少对于函数的同步部分。)
    • 这真的很有帮助。完成这个解决方案实际上帮助了我处理代码的其他方面,并将我推向另一个层次,了解如何实现 Promises 并在另一个抽象层次上理解它们。
    【解决方案3】:

    我已修改您的代码以使用 Q(NPM 模块) 承诺。 我假设您在上面 sn-p 中指定的“getLastRecord()”函数工作正常。

    您可以参考以下链接获取Q模块

    Click here : Q documentation

    var q = require('q');
    
    function getLastRecord(name)
    {
    
    var deferred = q.defer(); // Use Q 
    var connection = getMySQL_connection();
    
    var query_str =
    "SELECT name, " +
    "FROM records " +   
    "WHERE (name = ?) " +
    "LIMIT 1 ";
    
    var query_var = [name];
    
    var query = connection.query(query_str, query_var, function (err, rows, fields) {
        //if (err) throw err;
        if (err) {
            //throw err;           
            deferred.reject(err);
        }
        else {
            //console.log(rows);           
            deferred.resolve(rows);
        }
    }); //var query = connection.query(query_str, function (err, rows, fields) {
    
    return deferred.promise;
    }
    
    
    
    // Call the method like this
    getLastRecord('name_record')
     .then(function(rows){
       // This function get called, when success
       console.log(rows);
      },function(error){
       // This function get called, when error
       console.log(error);
    
     });
    

    【讨论】:

    • 请帮助:我遇到了“错误 [ERR_HTTP_HEADERS_SENT]:在将标头发送到客户端后无法设置标头”,我花了很多时间尝试解决此问题但未成功。我尝试了@Piyush Sagar 的方法,但仍然出现错误。我的服务器代码正在使用 next.js 包装器 arroung express,它使用他们称为 RequestHandler。这个请求处理程序似乎是在承诺解决之前返回响应。通过回调,我使用 if/else 设置响应数据。这就是我得到错误的地方,即使使用建议的承诺方法。
    【解决方案4】:

    回答您最初的问题:如何在 node.js 中以可读的方式完成?

    有一个名为co 的库,它使您可以在同步工作流中编写异步代码。看看npm install co

    使用这种方法时您经常遇到的问题是,您无法从您喜欢使用的所有库中返回 Promise。因此,您要么自己包装它(参见@Joshua Holbrook 的回答),要么寻找包装器(例如:npm install mysql-promise

    (顺便说一句:它在 ES7 的路线图中使用关键字 async await 为这种类型的工作流提供原生支持,但它还没有在节点中:node feature list。)

    【讨论】:

      【解决方案5】:

      这可以很简单地实现,例如使用 bluebird,如您所问:

      var Promise = require('bluebird');
      
      function getLastRecord(name)
      {
          return new Promise(function(resolve, reject){
              var connection = getMySQL_connection();
      
              var query_str =
                  "SELECT name, " +
                  "FROM records " +
                  "WHERE (name = ?) " +
                  "LIMIT 1 ";
      
              var query_var = [name];
      
              var query = connection.query(query_str, query_var, function (err, rows, fields) {
                  //if (err) throw err;
                  if (err) {
                      //throw err;
                      console.log(err);
                      logger.info(err);
                      reject(err);
                  }
                  else {
                      resolve(rows);
                      //console.log(rows);
                  }
              }); //var query = connection.query(query_str, function (err, rows, fields) {
          });
      }
      
      
      getLastRecord('name_record')
          .then(function(rows){
              if (rows > 20) {
                  console.log("action");
              }
          })
          .error(function(e){console.log("Error handler " + e)})
          .catch(function(e){console.log("Catch handler " + e)});
      

      【讨论】:

        【解决方案6】:

        我是 Node.js 的新手并承诺。我一直在寻找可以满足我需求的东西,这就是我在结合我找到的几个示例后最终使用的东西。我希望能够获取每个查询的连接并在查询完成后立即释放它(querySql),或者从池中获取连接并在 Promise.using 范围内使用它,或者在我想要的时候释放它(@987654322 @)。 使用这种方法,您可以一个接一个地连接多个查询,而无需嵌套它们。

        db.js

        var mysql = require('mysql');
        var Promise = require("bluebird");
        
        Promise.promisifyAll(mysql);
        Promise.promisifyAll(require("mysql/lib/Connection").prototype);
        Promise.promisifyAll(require("mysql/lib/Pool").prototype);
        
        var pool = mysql.createPool({
            host: 'my_aws_host',
            port: '3306',
            user: 'my_user',
            password: 'my_password',
            database: 'db_name'
        });
        
        function getSqlConnection() {
            return pool.getConnectionAsync().disposer(function (connection) {
                console.log("Releasing connection back to pool")
                connection.release();
            });
        }
        
        function querySql (query, params) {
            return Promise.using(getSqlConnection(), function (connection) {
                console.log("Got connection from pool");
                if (typeof params !== 'undefined'){
                    return connection.queryAsync(query, params);
                } else {
                    return connection.queryAsync(query);
                }
            });
        };
        
        module.exports = {
            getSqlConnection : getSqlConnection,
            querySql : querySql
        };
        

        usage_route.js

        var express = require('express');
        var router = express.Router();
        
        var dateFormat = require('dateformat');
        var db = require('../my_modules/db');
        var getSqlConnection = db.getSqlConnection;
        var querySql = db.querySql;
        
        var Promise = require("bluebird");
        
        function retrieveUser(token) {
          var userQuery = "select id, email from users where token = ?";
          return querySql(userQuery, [token])
             .then(function(rows){
                if (rows.length == 0) {
                  return Promise.reject("did not find user");
                }
        
                var user = rows[0];
                return user;
             });
        }
        
        router.post('/', function (req, res, next) {
        
          Promise.resolve().then(function () {
            return retrieveUser(req.body.token);
          })
            .then(function (user){
              email = user.email;
              res.status(200).json({ "code": 0, "message": "success", "email": email});
            })
            .catch(function (err) {
              console.error("got error: " + err);
              if (err instanceof Error) {
                res.status(400).send("General error");
              } else {
                res.status(200).json({ "code": 1000, "message": err });
              }
            });
        });
        
        module.exports = router;
        

        【讨论】:

        • 这是非常模块化和可重复使用的。
        • 使用var 的做法非常糟糕,但除此之外非常可重用
        • 谢谢!这正是我一直在寻找的!
        【解决方案7】:

        使用包 promise-mysql 的逻辑是使用 then(function(response){your code}) 链接承诺

        catch(function(response){your code}) 从 catch 块之前的“then”块中捕获错误。

        按照此逻辑,您将在块末尾使用 return 将查询结果传递到对象或数组中。返回将有助于将查询结果传递到下一个块。然后,结果将在函数参数中找到(这里是 test1)。使用此逻辑,您可以链接多个 MySql 查询和操作结果所需的代码并执行您想要的任何操作。

        Connection 对象被创建为全局对象,因为在每个块中创建的每个对象和变量都只是局部的。不要忘记你可以链接更多的“then”块。

        var config = {
            host     : 'host',
            user     : 'user',
            password : 'pass',
            database : 'database',
        
          };
          var mysql = require('promise-mysql');
          var connection;
          let thename =""; // which can also be an argument if you embed this code in a function
        
          mysql.createConnection(config
          ).then(function(conn){
              connection = conn;
              let test = connection.query('select name from records WHERE name=? LIMIT 1',[thename]);
              return test;
          }).then(function(test1){
              console.log("test1"+JSON.stringify(test1)); // result of previous block
              var result = connection.query('select * from users'); // A second query if you want
              connection.end();
         connection = {};
              return result;
          }).catch(function(error){
              if (connection && connection.end) connection.end();
              //logs out the error from the previous block (if there is any issue add a second catch behind this one)
              console.log(error);
          });
        

        【讨论】:

          【解决方案8】:

          我对 node 还是有点陌生​​,所以也许我错过了一些东西,让我知道它是如何工作的。与其触发异步节点,不如将其强加给您,因此您必须提前考虑并进行计划。

          const mysql = require('mysql');
          const db = mysql.createConnection({
                    host: 'localhost', 
                    user: 'user', password: 'password', 
                    database: 'database',
                });
                db.connect((err) => {
                    // you should probably add reject instead of throwing error
                    // reject(new Error()); 
                    if(err){throw err;}
                    console.log('Mysql: Connected');
                });
                db.promise = (sql) => {
                    return new Promise((resolve, reject) => {
                        db.query(sql, (err, result) => {
                          if(err){reject(new Error());}
                          else{resolve(result);}
                        });
                    });
                };
          

          这里我像往常一样使用 mysql 模块,但是我创建了一个新函数来提前处理 promise,方法是将它添加到 db const。 (在很多节点示例中,您将其视为“连接”。

          现在让我们使用 Promise 调用一个 mysql 查询。

                db.promise("SELECT * FROM users WHERE username='john doe' LIMIT 1;")
                .then((result)=>{
                    console.log(result);
                }).catch((err)=>{
                    console.log(err);
                });
          

          我发现这在您需要根据第一个查询进行第二个查询时很有用。

                db.promise("SELECT * FROM users WHERE username='john doe' LIMIT 1;")
                .then((result)=>{
                    console.log(result);
                    var sql = "SELECT * FROM friends WHERE username='";
                        sql = result[0];
                        sql = "';"
                    return db.promise(sql);
                }).then((result)=>{
                    console.log(result);
                }).catch((err)=>{
                    console.log(err);
                });
          

          你应该实际使用 mysql 变量,但这至少应该给你一个在 mysql 模块中使用 Promise 的例子。

          此外,您仍然可以在这些承诺中的任何时候继续以正常方式使用 db.query,它们就像正常工作一样。

          希望这对死亡三角有所帮助。

          【讨论】:

            【解决方案9】:

            可能对其他人有帮助,扩展@Dillon Burnett 的答案

            使用 async/await 和参数

            db.promise = (sql, params) => {
                return new Promise((resolve, reject) => {
                  db.query(sql,params, (err, result) => {
                      if(err){reject(new Error());}
                         else{resolve(result);}
                      });
                   });
            };
            module.exports = db;
            
               
            
             async connection(){
                const result = await db.promise("SELECT * FROM users WHERE username=?",[username]);
                   return result;
                }
            

            【讨论】:

              猜你喜欢
              • 2015-03-04
              • 2014-12-15
              • 2020-05-22
              • 1970-01-01
              • 1970-01-01
              • 2014-02-01
              • 2020-07-11
              • 1970-01-01
              相关资源
              最近更新 更多