使用 promise 处理 MySQL return 中的值 node.js
Use promise to process MySQL return value in node.js
我有 python 背景,目前正在迁移到 node.js。由于其异步性质,我无法适应 node.js。
例如,我正在尝试 return 来自 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 的异步性质,我需要 return 一个承诺。我不能像 python 那样编写 node.js 代码。如何将 getLastRecord()
转换为 return 承诺以及如何处理 returned 值?
其实我想做的事情是这样的;
if (getLastRecord() > 20)
{
console.log("action");
}
如何以可读的方式在 node.js 中做到这一点?
我想看看在这种情况下如何使用 bluebird 实现承诺。
你不需要使用 promises,你可以使用回调函数,像这样:
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
}
});
这里有点乱,见谅
首先,假设此代码正确使用 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)
中的函数参数,node 中看到的 (err, result)
函数以及 promise 结果和错误处理程序都是回调。人们将这种特定类型的回调称为 "callbacks," 节点核心中使用的 (err, result)
的回调有些混淆,在所谓的 "continuation-passing style" 中,有时被人们称为 "nodebacks"不太喜欢他们
至少现在(async/await 最终会出现),无论您是否采用 promises,您几乎都受困于回调。
另外,我会注意到 promises 不是立即的,显然在这里很有用,因为你还有一个回调。只有当您将 Promise 与 Promise.all
结合起来并像 Array.prototype.reduce
一样使用 Promise 累加器时,Promises 才会真正闪耀。但他们 确实 有时会发光,他们 值得学习。
我已修改您的代码以使用 Q(NPM 模块)承诺。
我假设您在上面的代码片段中指定的 'getLastRecord()' 函数正常工作。
您可以参考以下link获取Q模块
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);
});
回答您最初的问题:如何以可读的方式在 node.js 中完成?
有一个名为 co
的库,它使您可以在同步工作流中编写异步代码。看看就知道了 npm install co
.
您使用该方法经常面临的问题是,您无法从您喜欢使用的所有库中获得 Promise
返回值。所以你要么自己包装它(参见@Joshua Holbrook 的回答)要么寻找一个包装器(例如:npm install mysql-promise
)
(顺便说一句:它在 ES7 的路线图上使用关键字 async
await
原生支持这种类型的工作流,但它还没有在节点中:node feature list。 )
这可以很简单地实现,例如使用蓝鸟,如您所问:
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)});
我是 Node.js 的新手,我保证。我一直在寻找能够满足我需求的东西,这就是我在结合我发现的几个例子后最终使用的东西。我希望能够获取每个查询的连接并在查询完成后立即释放它 (querySql
),或者从池中获取连接并在 Promise.using 范围内使用它,或者在我想要的时候释放它它(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;
使用包 promise-mysql 的逻辑是使用 then(function(response){your code})
链接承诺
和
catch(function(response){your code}) 从 catch 块之前的 "then" 块捕获错误。
按照此逻辑,您将在块末尾使用 return 以对象或数组形式传递查询结果。 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);
});
我对节点还是有点陌生,所以也许我错过了一些让我知道它是如何工作的。而不是触发异步节点只是将它强加给你,所以你必须提前考虑并计划它。
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。 (在很多节点示例中,您将其视为 "connection"。
现在让我们使用 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 模块的承诺的例子。
此外,在这些承诺中,您仍然可以随时继续使用 db.query 正常方式,它们就像正常工作一样。
希望这对死亡三角有帮助。
扩展@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;
}
我有 python 背景,目前正在迁移到 node.js。由于其异步性质,我无法适应 node.js。
例如,我正在尝试 return 来自 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 的异步性质,我需要 return 一个承诺。我不能像 python 那样编写 node.js 代码。如何将 getLastRecord()
转换为 return 承诺以及如何处理 returned 值?
其实我想做的事情是这样的;
if (getLastRecord() > 20)
{
console.log("action");
}
如何以可读的方式在 node.js 中做到这一点?
我想看看在这种情况下如何使用 bluebird 实现承诺。
你不需要使用 promises,你可以使用回调函数,像这样:
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
}
});
这里有点乱,见谅
首先,假设此代码正确使用 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)
中的函数参数,node 中看到的 (err, result)
函数以及 promise 结果和错误处理程序都是回调。人们将这种特定类型的回调称为 "callbacks," 节点核心中使用的 (err, result)
的回调有些混淆,在所谓的 "continuation-passing style" 中,有时被人们称为 "nodebacks"不太喜欢他们
至少现在(async/await 最终会出现),无论您是否采用 promises,您几乎都受困于回调。
另外,我会注意到 promises 不是立即的,显然在这里很有用,因为你还有一个回调。只有当您将 Promise 与 Promise.all
结合起来并像 Array.prototype.reduce
一样使用 Promise 累加器时,Promises 才会真正闪耀。但他们 确实 有时会发光,他们 值得学习。
我已修改您的代码以使用 Q(NPM 模块)承诺。 我假设您在上面的代码片段中指定的 'getLastRecord()' 函数正常工作。
您可以参考以下link获取Q模块
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);
});
回答您最初的问题:如何以可读的方式在 node.js 中完成?
有一个名为 co
的库,它使您可以在同步工作流中编写异步代码。看看就知道了 npm install co
.
您使用该方法经常面临的问题是,您无法从您喜欢使用的所有库中获得 Promise
返回值。所以你要么自己包装它(参见@Joshua Holbrook 的回答)要么寻找一个包装器(例如:npm install mysql-promise
)
(顺便说一句:它在 ES7 的路线图上使用关键字 async
await
原生支持这种类型的工作流,但它还没有在节点中:node feature list。 )
这可以很简单地实现,例如使用蓝鸟,如您所问:
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)});
我是 Node.js 的新手,我保证。我一直在寻找能够满足我需求的东西,这就是我在结合我发现的几个例子后最终使用的东西。我希望能够获取每个查询的连接并在查询完成后立即释放它 (querySql
),或者从池中获取连接并在 Promise.using 范围内使用它,或者在我想要的时候释放它它(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;
使用包 promise-mysql 的逻辑是使用 then(function(response){your code})
链接承诺和
catch(function(response){your code}) 从 catch 块之前的 "then" 块捕获错误。
按照此逻辑,您将在块末尾使用 return 以对象或数组形式传递查询结果。 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);
});
我对节点还是有点陌生,所以也许我错过了一些让我知道它是如何工作的。而不是触发异步节点只是将它强加给你,所以你必须提前考虑并计划它。
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。 (在很多节点示例中,您将其视为 "connection"。
现在让我们使用 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 模块的承诺的例子。
此外,在这些承诺中,您仍然可以随时继续使用 db.query 正常方式,它们就像正常工作一样。
希望这对死亡三角有帮助。
扩展@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;
}