Fat*_*tie 5 mysql nested node.js express
我有一个普通
var express = require('express')
Run Code Online (Sandbox Code Playgroud)
Node 表示 www 页面,像往常一样使用 session、pug 等。我的数据库调用
var db = require('./scripts/myHappyMysqlScript')
Run Code Online (Sandbox Code Playgroud)
我很自然地使用mysql,所以在 db 脚本中
var mysql = require('mysql')
Run Code Online (Sandbox Code Playgroud)
所以例如
app.get('/catPhotos', (req, response) => {
response.render('catPhotos.pug');
})
Run Code Online (Sandbox Code Playgroud)
假设一个页面有一个表,显示了 petNames 数据库中的内容,
app.get('/pets', function(req, res, next) {
db.allPetNames(function(err, petsList) {
res.render('pets.pug',
{
'petsList': petsList,
'pretty' : true
})
})
Run Code Online (Sandbox Code Playgroud)
到目前为止一切都很好。
但是这里有一个例子,在 pug 页面上有三个表,以及三个不同的数据库调用:
db.cats(function(err, c) {
db.dogs(function(err, d) {
db.budgies(function(err, b) {
res.render('bigScreen.pug',
{
'cats' : c,
'k9s': d,
'budgies': b,
'pretty' : true
})
})
})
})
Run Code Online (Sandbox Code Playgroud)
我只是这样嵌套它们。
这似乎工作得很好。
它正确地按顺序等待。错误发生并得到妥善处理,等等。
但是有没有更好的语法,更好的方法?对于真正的™ Node 而非 Swift 程序员来说,Node Way 是什么?!
也许考虑到我正在使用mysql图书馆,如果这是相关的。
请注意,总体而言,一种更好的方法是使用 Ajax 之类的东西在网页的每个“部分”中进行流式传输。事实上,我一直都在这样做。我在这里问的是,假设在 res.render 我确实想一次返回所有信息,有什么比这样嵌套更好的吗?干杯
You*_*saf 10
您可以使用promises.
由于您提到您正在使用mysql库与数据库进行交互,不幸的是,该库不提供基于承诺的 API。因此,要摆脱代码中的嵌套数据库调用,您需要围绕数据库调用的回调版本创建一个基于 Promise 的包装器。
有关什么是 Promise 及其工作方式的一般概述,请参阅以下链接:
下面是一个示例,说明如何创建基于 Promise 的包装器,然后使用该包装器摆脱嵌套的数据库调用。
这个基于承诺的包装器只是一个返回承诺的函数。它创建一个 promise 实例,包装底层数据库调用,最终当数据库调用返回数据时,它会通知您的代码。
function getCats() {
return new Promise((resolve, reject) => {
// make the database call
db.cats((error, cats) => {
// in case of an error, reject the promise by
// calling "reject" function
// Also pass the "error" object to the "reject" function
// as an argument to get access to the error message
// in the code that calls this "getCats" function
if (error) {
reject(error);
return;
}
// if there was no error, call "resolve" function
// to resolve the promise. Promise will be resolved
// in case of successful database call
// Also pass the data to "resolve" function
// to access this data in the code that calls this
// "getCats" function
resolve(cats);
});
});
}
Run Code Online (Sandbox Code Playgroud)
现在在您的路由处理程序函数中,不是调用db.cats(...),而是调用这个getCats包装函数。
有两种方法可以调用返回承诺的函数:
Promise-chaining (有关详细信息,请访问上述链接)async-await 语法(推荐)以下代码示例使用async-await语法。为此,首先async使用async关键字前的function关键字将路由处理程序函数标记为。这样做,我们可以await在这个路由处理函数中使用关键字。
app.get('/pets', async function(req, res, next) {
try {
const cats = await getCats();
// similar wrappers for other database calls
const dogs = await getDogs();
const budgies = await getBudgies();
// render the pub template, passing in the data
// fetched from the database
...
catch (error) {
// catch block will be invoked if the promise returned by
// the promise-based wrapper function is rejected
// handle the error appropriately
}
});
Run Code Online (Sandbox Code Playgroud)
上面的代码示例仅显示了如何将db.cats(...)数据库调用包装在基于 Promise 的包装器中,并使用该包装器从数据库中获取数据。同样,您可以为db.dogs(...)和db.budgies(...)调用创建包装器。
理想情况下,与其为每个数据库调用创建一个单独的基于 Promise 的包装器,不如创建一个可重用的基于 Promise 的包装器函数,该函数接受一个函数来调用并将该函数调用包装在一个承诺中,就像上面的代码所示例如,即getCats功能。
在路由处理函数中的上述代码中需要注意的一件重要事情
const cats = await getCats();
const dogs = await getDogs();
const budgies = await getBudgies();
Run Code Online (Sandbox Code Playgroud)
是这将导致顺序数据库调用,这可能是您想要的,也可能不是。
如果这些数据库调用不相互依赖,那么您可以使用Promise.all()方法并行调用基于承诺的包装器。
以下代码示例显示了如何使用Promise.all().
app.get('/pets', async function(req, res, next) {
try {
// "petsData" will be an array that will contain all the data from
// three database calls.
const petsData = await Promise.all([getCats(), getDogs(), getBudgies()]);
// render the pub template, passing in the data
// fetched from the database
...
catch (error) {
...
}
});
Run Code Online (Sandbox Code Playgroud)
我希望这足以帮助您摆脱当前代码中的嵌套数据库调用,并开始在代码中使用 promise。