我在Nodejs的应用程序中设计与MySQL数据库的通信时遇到问题。 最大的问题是查询是异步的,因此设计我的项目变得很复杂。例如,我有excercises.js
EXCERCISES.JS
var express = require('express');
var database = require('../database/database.js');
var router = express.Router();
console.log(database.db(saveDbData))
/* GET users listing. */
router.get('/', function(req, res, next) {
res.render('exercises',{title: 'Exercises', ex: #DATABASE RESULT});
});
module.exports = router;
需要在 ex 字段中写入查询结果。
然后我编写一个模块来处理mysql连接
DATABASE.JS
var pool = mysql.createPool({
connectionLimit: 10000,
host: 'localhost',
user: 'root',
password: 'password',
database: 'Example'
});
var results;
var db = function(){
pool.query('SELECT name FROM Exercises', function(error, results, fields){
if (error) throw error;
res = results;
})
return res;
}
module.exports = {
db: db,
}
很显然,它不起作用,因为pool.query是异步的。 我在网上找到的唯一替代方法是这样的:
EXERCISES.JS
var pool = mysql.createPool({
connectionLimit: 10000,
host: 'localhost',
user: 'root',
password: 'password',
database: 'Example'
});
pool.query('SELECT name FROM Exercises', function(error, results, fields){
if (error) throw error;
router.get('/', function(req, res, next) {
res.render('exercises',{title: 'Exercises', ex: result[0].name});
});
})
但是以这种方式,mysql部分和路由/渲染部分混合在一起。它仍然是设计良好的解决方案吗?有更优雅的解决方案吗?
EDIT :
我已经修改了文件,并像这样使用Promise
EXERCISES.JS
var express = require('express');
var database = require('../database/database.js');
var router = express.Router();
var data = database.db()
.then(
function(data){
console.log("Resolved");
/* GET users listing. */
router.get('/', function(req, res, next) {
res.render('exercises',{title: 'Exercises', ex: data[0].name});
});
})
.catch(
error => console.error(error));
module.exports = router;
DATABASE.JS
ar mysql = require('mysql');
var pool = mysql.createPool({
connectionLimit: 10000,
host: 'localhost',
user: 'root',
password: 'password',
database: 'Example'
});
var res;
var db = function(){
return new Promise(function(resolve, reject){
pool.query('SELECT name FROM Exercises', function(error, results, fields){
if (error) reject(error);
resolve(results)
})
})
}
module.exports = {
db: db,
}
它有效,但是我认为这不是最佳解决方案。例如,如果我想从更多查询中获取用于渲染的数据该怎么办? 我是这些技术的新手,因此无法找出整合数据库和html页面呈现的最佳方法。
答案 0 :(得分:0)
您可以使用npm模块通过MySQL实现异步任务。 我建议选择 sequilize 或 jm-ez-mysql 。如果您使用 jm-ez-mysql ,那么您的代码结构就像
server.js
require('./config/database.js');
./ config / database.js
const sql = require('jm-ez-mysql');
// Init DB Connection
const connection = My.init({
host: process.env.DBHOST,
user: process.env.DBUSER,
password: process.env.DBPASSWORD,
database: process.env.DATABASE,
dateStrings: true,
charset: 'utf8mb4',
timezone: 'utc',
multipleStatements: true,
});
module.exports = {
connection,
};
之后,您可以异步使用MySQL。
./ exercise.js
const sql = require('jm-ez-mysql');
const exerciseUtil = {};
exerciseUtil.searchUserById = async (id) => {
try {
// table name, db fields, condition, values
const result = await sql.first('users', ['id, name, email'], 'id = ?', [id]);
return result; // Which return your result in object {}
} catch (err) {
console.log(err);
throw err;
}
};
module.exports = exerciseUtil;
我希望它会有所帮助。 快乐编码:)
答案 1 :(得分:0)
您是否想知道为什么节点中的所有Web框架都要求您使用res
对象而不是return
返回响应?这是因为所有Web框架都希望您需要执行异步操作。
考虑类似于Laravel(PHP)或Spring Framework(Java)的Web框架设计:
// Theoretical synchronous framework API:
app.get('/path', function (request) {
return "<html><body> Hello World </body></html>";
});
然后,如果您需要执行异步操作,则将面临以下问题:在您需要返回HTTP请求时,所获取的数据尚未返回:
// Theoretical synchronous framework API:
app.get('/path', function (request) {
return ??? // OH NO! I need to return now!!
});
因此,javascript中的Web框架不会对返回值起作用。相反,它会在完成后为您传递一个回调以供调用:
// Express.js
app.get('/path', function (request, response) {
doSomethingAsync((err, result) => {
response.send(result);
});
});
因此,对于您的代码,您只需要执行以下操作:
router.get('/', function(req, res) {
pool.query('SELECT name FROM Exercises', function(error, results, fields){
if (error) throw error;
res.render('exercises',{title: 'Exercises', ex: result[0].name});
});
});
导出数据库就像导出pool
一样简单:
db.js
var pool = mysql.createPool({
connectionLimit: 10000,
host: 'localhost',
user: 'root',
password: 'password',
database: 'Example'
});
module.exports = {
pool: pool
}
exercises.js
let db = require('./db');
// you can now use db.pool in the rest of your code
// ..
您可以(并且应该)在您的数据库模块中对它们进行编码,而不是在控制器(路由)中对SELECT语句进行编码:
db.js
var pool = mysql.createPool({
connectionLimit: 10000,
host: 'localhost',
user: 'root',
password: 'password',
database: 'Example'
});
function getExerciseNames (callback) {
pool.query('SELECT name FROM Exercises',callback);
}
module.exports = {
pool: pool
}
然后,您只需在控制器逻辑中执行以下操作即可:
router.get('/', function(req, res) {
db.getExerciseNames(function(error, results, fields){
if (error) throw error;
res.render('exercises',{
title: 'Exercises',
ex: result[0].name
});
});
});
如果您打算仅一次查询数据库以缓存Exercises的值,则不要反转Express路由流程。而是在您的数据库层实现缓存:
db.js :
var exerciseNamesCache = [];
var exerciseNamesFields = [];
function getExerciseNames (callback) {
if (exerciseNamesCache.length > 0 && exerciseNamesFields.length > 0) {
callback(null, exerciseNamesCache, exerciseNamesFields);
}
pool.query('SELECT name FROM Exercises',function(error, results, fields){
if (!error) {
exerciseNamesCache = results;
exerciseNamesFields = fields;
}
callback(error, results, fields);
});
}
Promise是用于处理回调的设计模式。它仅比Java的Futures(CompletionStage等)轻巧得多。如果您使用的API返回承诺而不是接受回调,那么您需要在承诺的res.render()
方法内调用.then()
:
router.get('/', function(req, res, next) {
doSomethingAsync()
.then(function(result){
res.send(result);
})
.catch(next); // remember to pass on asynchronous errors to next()
});
如果您使用的API接受了回调,那么无论是否天气,您将其包装在Promise中都是一个问题。我个人不会这样做,除非您还使用另一个返回承诺的API。
promise的一个优点是您可以将它们与await
一起使用。 Express特别适用于异步/等待。请记住,您只能在标有await
的函数内使用async
:
router.get('/', async function(req, res, next) {
let result = await doSomethingAsync();
res.send(result);
});
获取多个异步数据可以很简单:
router.get('/', function(req, res, next) {
doSomethingAsync(function(result1){
doSomethingElse(function(result2) {
res.json([result1, result2]);
});
});
});
承诺如下:
router.get('/', function(req, res, next) {
doSomethingAsync()
.then(function(result1){
return doSomethingElse()
.then(function(result2) {
return [result1, result2];
});
})
.then(function(results){
res.json(results);
})
.catch(next);
});
但是以上两个代码都依次执行请求(先获取result1,然后再获取result2)。如果要并行获取两个数据,可以使用Promises:
router.get('/', function(req, res, next) {
Promise.all([
doSomethingAsync(), // fetch in parallel
doSomethingElse()
])
.then(function(results){
res.json(results);
});
})
使用回调函数会更加复杂。您可以使用一种设计模式,实际上有人已经将其实现为名为async.js的库,但是通常最简单的解决方案是将它们包装在Promises中并使用Promise.all()
。不过,还是要检查一下async.js,因为它具有对批处理请求,条件为真时执行异步操作等有用的功能(该库的基于Promise的副本是async-q)