在Node-JS中使用promise-object的解析数据来渲染一个数据填充的网页



我是Node-JS的新手,试图掌握异步概念和承诺。我正在努力确保数据库数据的可用性在此刻的web服务器正在被渲染。

随着代码,我似乎已经实现了正确的定时-函数buildhtml被调用,当数据可用。经过一夜的研究,这是第一次成功。

现在我正试图找到将数据传递给web服务器/express的正确方法。

我正在寻找的数据现在被封装在promise-object中,无法处理。我看了很多其他的帖子,但没有能够推导出一个解决方案的例子。

问题:以消费方式(基本上是承诺对象,但没有外部承诺封装)交付数据的最佳实践解决方案是什么?是我的想法完全错了,还是我只是少了一小步?任何答案都是非常感激的,我提前感谢!

//DB Load Users
const promise = new Promise((resolve, reject) => {
db.connect(function(err) {
if (err) throw err;
console.log("Connected!");
var sql = "SELECT username, useremail, userage, useruniqueid FROM users";
db.query(sql, function(err, result) {
if (err) throw err;
const jsonresult = JSON.parse(JSON.stringify(result)); //to clear RowDatePacket
resolve(jsonresult);
});
})
})
promise
.then(buildhtml)
//Render (express) HTML with promise resolve after DB-query
function buildhtml() {
app.get("/", function(req, res) {
res.render("home", {
data: promise
})
})
}

这是buildhtml()中console.log(promise)的输出:

Connected!
Promise {
[
{
username: 'admin',     
useremail: '888883895',
userage: '33',
useruniqueid: 1        
},
{
username: 'admin',     
useremail: '888883895',
userage: '22',
useruniqueid: 2
},
{
username: 'admin',
useremail: '888883895',
userage: '22',
useruniqueid: 3
},
{
username: 'admin',
useremail: '888883895',
userage: '22',
useruniqueid: 4
},
{
username: 'pushy boi',
useremail: 'pb@gmail.com',
userage: '65',
useruniqueid: 5
},
{
username: 'push attempt',
useremail: 'pb@gmail.com',
userage: '65',
useruniqueid: 6
},
{
username: 'instead of push db',
useremail: 'db@gmail.com',
userage: '37',
useruniqueid: 104
},
{
username: '111',
useremail: '111',
userage: '11',
useruniqueid: 111
},
{
username: null,
useremail: null,
userage: null,
useruniqueid: 112
},
{
username: 'Max Mustermann',
useremail: 'MM@gmail.com',
userage: '44',
useruniqueid: 333
}
]
}

大致应该这样重写。首先,将获取MySQL数据的代码封装在一个函数中。

function getUsers() {
return new Promise((resolve, reject) => {
db.connect(function(err) {
if (err) reject(err);
console.log("Connected!");
var sql = "SELECT username, useremail, userage, useruniqueid FROM users";
db.query(sql, function(err, result) {
if (err) reject(err);
resolve(result[0]);
});
})
})
}

注意我将throws更改为reject,并删除了不需要的JSON步骤。

如果您使用基于承诺的mysql库(如mysql2/promises)并使用连接池,则更好:

async function getUsers() {
const result = await pool.query("SELECT username, useremail, userage, useruniqueid FROM users");
return result[0];
}

相比之下太干净了!

然后定义路由并在需要时调用getUsers:

app.get("/", function(req, res) {
getUsers()
.then(result => {
res.render("home", {
data: result
})
})
.catch(err => {
res.status(500);
console.error(err);    
});
});

您得到的应该工作,但引入了潜在的竞争条件…如果在承诺解决之前向GET /发出请求怎么办?Express将简单地返回404,因为它不知道路由。

与其在承诺解析后注册你的路由处理程序,不如在路由处理程序中解析承诺

app.get("/", async (_req, res, next) => {
try {
res.render("home", { data: await promise });
} catch (err) {
next(err);
}
});

请注意,您的查询将只在Express服务器启动时执行一次。这可能是你想要的,也可能不是。更典型的方法是在每次发出请求时执行查询。