2016-04-11 18 views
14

我有一个Python背景,目前正在迁移到node.js.由于其异步性质,我有调整到node.js的问题。使用promise来处理node.js中的MySQL返回值

例如,我试图从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中的异步特性。我不能像python一样编写node.js代码。如何将getLastRecord()转换为承诺并如何处理返回的值?

事实上,我想要做的就是这样的事情;

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

这怎么可以在node.js以可读的方式完成?

我想看看在这种情况下如何使用蓝鸟实现承诺。

回答

20

这会是一个小散,原谅我。

首先,假设这段代码正确地使用MySQL驱动程序API,这里是你可以将它包装与原生承诺工作的一种方式:

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)(err, result)函数中的函数参数以及promise结果和错误处理程序都是回调函数。这被某些人称为“回调”,在节点核心中使用的称为“延续传递风格”的(err, result),有时称为“回调”,有时候被那些不太喜欢它们的人称为“节点回放” 。

现在,至少(异步/等待最终会到来),无论您是否采用承诺,您都几乎被回调困住。

此外,我会注意到承诺并不是立即显示,在这里显然很有帮助,因为您仍然有回调。当你将它们与Promise.all结合在一起时,承诺才会真正发光,并且承诺累积器可以使用la Array.prototype.reduce。但他们有时候闪耀,他们值得学习。

+0

哦,如果你确实使用promise,请考虑bluebird!它具有更好的可读性。一些好帮手,很好理解的表演ormance等 –

+0

如果我使用蓝鸟,我可以拿我的'getLastRecord()'函数做类似'Promisify(getLastRecord)'和'getLastRecord()'的支持诺言吗? – user781486

+0

我认为http://bluebirdjs.com/docs/api/promise.fromcallback.html是你想要的 –

3

你并不需要使用的承诺,你可以使用一个回调函数,这样的事情:

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 

    } 
}); 
+0

感谢。有没有办法做这样的事情'if(getLastRecord()> 20>'或者至少让它可读? – user781486

+1

@ user16891328你必须在回调中执行'getLastRecord('name_record',function(err,数据){if(err){} else {if(data.length> 20)}});' –

+0

好的,谢谢,好像没有其他的选择了,代码的可读性比python差 – user781486

5

我修改了您的代码以使用Q(NPM模块)承诺。 I假设您在上面代码片段中指定的'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); 

}); 
3

要回答你最初的问题的保持:如何能在这个node.js的以可读的方式进行?

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

你经常用这种方法面临的问题是,你没有从你喜欢使用的所有库中取回Promise。所以你要么自己包装它(看@Joshua Holbrook的答案),要么寻找一个包装器(例如:npm install mysql-promise

(顺便说一句:它的ES7路线图有这种类型的工作流的本地支持关键字asyncawait,但其尚未节点:node feature list

2

这可以非常简单地实现,例如与蓝鸟,你问:

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)}); 
4

我是新来的NodeJS和promisses。我正在寻找一些能满足我需求的东西,这是我结合我发现的几种做法后最终使用的。我希望能够在查询结束后立即获取每个查询的连接并释放它(querySql),或者从池中获取连接并在Promise中使用它。使用范围或在需要时释放它(getSqlConnection)。 使用这种方法,您可以将多个查询一个接一个地串联在一起,而不需要嵌套它们。

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; 
+0

这是相当模块化和可重复使用的。 – Milind