您可以使用promises 摆脱嵌套的数据库调用。
由于您提到您正在使用mysql 库与数据库进行交互,不幸的是,该库不提供基于 Promise 的 API。因此,要摆脱代码中的嵌套数据库调用,您需要围绕数据库调用的回调版本创建一个基于 Promise 的包装器。
有关什么是 Promise 及其工作原理的总体概述,请参阅以下链接:
以下是一个示例,说明如何创建基于 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);
});
});
}
现在在您的路由处理函数中,调用 getCats 包装函数,而不是调用 db.cats(...)。
有两种方法可以调用返回承诺的函数:
-
Promise-chaining(详情请访问上述链接)
-
async-await 语法(推荐)
以下代码示例使用async-await 语法。为此,首先在function 关键字之前使用async 关键字将路由处理函数标记为async。这样做,我们可以在这个路由处理函数中使用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
}
});
以上代码示例仅展示了如何将db.cats(...) 数据库调用包装在基于承诺的包装器中,并使用该包装器从数据库中获取数据。同样,您可以为 db.dogs(...) 和 db.budgies(...) 调用创建包装器。
最好不要为每个数据库调用创建一个单独的基于 Promise 的包装器,理想情况下,您应该创建一个可重用的基于 Promise 的包装器函数,它接收要调用的函数并包装该函数调用在一个promise中,就像上面的代码示例所示,即getCats函数。
并行数据库调用
在路由处理函数的上述代码中需要注意的一点
const cats = await getCats();
const dogs = await getDogs();
const budgies = await getBudgies();
这会导致连续的数据库调用,这可能是你想要的,也可能不是你想要的。
如果这些数据库调用不相互依赖,那么您可以使用Promise.all() 方法并行调用基于promise 的包装器。
以下代码示例展示了如何使用 Promise.all() 并行调用基于 Promise 的包装函数。
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) {
...
}
});
我希望这足以帮助您摆脱当前代码中的嵌套数据库调用,并开始在您的代码中使用 Promise。