在node.js中使用promise处理MySQL返回值

Posted

技术标签:

【中文标题】在node.js中使用promise处理MySQL返回值【英文标题】:Use promise to process MySQL return value in node.js 【发布时间】: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。

【问题讨论】:

【参考方案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 吗? 我认为bluebirdjs.com/docs/api/promise.fromcallback.html是你想要的 顺便说一句: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 Hol*** 的回答),要么寻找包装器(例如: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());
                elseresolve(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());
             elseresolve(result);
          );
       );
;
module.exports = db;

   

 async connection()
    const result = await db.promise("SELECT * FROM users WHERE username=?",[username]);
       return result;
    

【讨论】:

以上是关于在node.js中使用promise处理MySQL返回值的主要内容,如果未能解决你的问题,请参考以下文章

在Node.js使用Promise的方式操作Mysql

在 Node.js 中用 Promise 替换回调

如何在 Node.js 流回调中聚合从异步函数生成的 Promise?

node.js 模块:Async vs Fibers.promise vs Q_oper8

使用 Q 在 Node.js 中同步 Promise 的麻烦

如何让我的 Node.js MySQL 连接作为承诺工作?