在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)
,如果不是catch
ed。 (至少对于函数的同步部分。)
这真的很有帮助。完成这个解决方案实际上帮助了我处理代码的其他方面,并将我推向另一个层次,了解如何实现 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?
node.js 模块:Async vs Fibers.promise vs Q_oper8