
在构建基于 express 的 api 服务时,我们经常需要从多个数据库表中获取数据,并将其组合成一个复杂的 json 结构返回给客户端。例如,一个电影详情接口可能需要返回电影的基本信息(如标题、年份)以及该电影的所有主要演职人员信息。当这些演职人员信息存储在另一个独立的表中时,就需要进行两次数据库查询,并将第二次查询的结果嵌套到第一次查询的结果中。
常见的错误模式是在处理主数据查询结果的 map 回调函数中,直接嵌入另一个异步查询(如使用 .then())。由于 Array.prototype.map() 方法是同步执行的,它不会等待内部的 Promise 解析。这意味着,当 map 尝试构建对象时,嵌套的异步查询可能尚未完成,或者返回的是一个未解析的 Promise 对象,而不是实际的数据。最终,这会导致在 JSON 响应中,嵌套的数据部分(如 principals 字段)显示为空对象 {},而不是期望的数组。
考虑以下错误示例:
router.get('/movies/data/:imdbID', function(req, res, next) {
const queryMovie = req.db.from('basics').select(/* ... */).where('tconst', req.params.imdbID);
const queryPrincipals = req.db.from('principals').select(/* ... */).where('tconst', req.params.imdbID);
queryMovie.then((movieData) => {
const movie = movieData.map(data => {
return {
// ...其他电影数据
principals: queryPrincipals.then((principals) => { // 错误:map不会等待这个Promise
return principals.map(principal => { /* ... */ });
}),
// ...
}
});
res.json(movie); // 此时principals可能还是一个未解析的Promise或空对象
});
});在这个例子中,map 函数在 queryPrincipals.then() 完成之前就返回了其内部对象。principals 字段因此不会包含实际的演职人员数据。
为了正确处理这种嵌套的异步数据获取场景,我们应该使用 JavaScript 的 async/await 语法。async/await 允许我们以同步的方式编写异步代码,使得代码更易读、更易于理解和维护。
核心思想是:
Easily find JSON paths within JSON objects using our intuitive Json Path Finder
30
以下是使用 async/await 修正后的代码示例:
router.get('/movies/data/:imdbID', async function(req, res, next) {
try {
// 1. 定义电影基本信息查询
const queryMovie = req.db.from('basics')
.select(
'primaryTitle',
'year',
'runtimeMinutes',
'genres',
'country',
'boxoffice',
'poster',
'plot'
)
.where('tconst', req.params.imdbID);
// 2. 定义演职人员信息查询
const queryPrincipals = req.db.from('principals')
.select('nconst', 'category', 'name', 'characters')
.where('tconst', req.params.imdbID);
// 3. 等待电影基本信息查询结果
const movieData = await queryMovie; // Knex查询本身返回Promise
// 4. 映射电影数据,并在内部处理演职人员数据
// 注意:如果movieData是数组,且我们只期望一个结果,可以考虑使用.first()或直接处理第一个元素
const result = await Promise.all(movieData.map(async ({
primaryTitle: title,
year,
runtimeMinutes: runtime,
genres,
country,
boxoffice,
poster,
plot
}) => {
// 在map回调内部,等待演职人员查询结果
// 注意:这里queryPrincipals()调用后,需要await等待其结果
const principalsRaw = await queryPrincipals; // Knex查询本身返回Promise
const principals = principalsRaw.map(
({
nconst: id,
category,
name,
characters
}) => ({
id,
category,
name,
// characters字段在数据库中可能是字符串,如果需要数组,可能需要进一步处理
characters: characters ? JSON.parse(characters) : []
})
);
return {
title,
year,
runtime,
genres: genres ? JSON.parse(genres) : [], // 假设genres在数据库中是JSON字符串
country,
principals,
boxoffice,
poster,
plot
};
}));
// 5. 发送JSON响应
// 如果movieData通常只返回一个电影,可以直接返回result[0]
res.json(result.length > 0 ? result[0] : {});
} catch (error) {
// 6. 错误处理
console.error('获取电影数据失败:', error);
next(error); // 将错误传递给Express错误处理中间件
}
});代码解析与注意事项:
通过采用 async/await 模式,我们可以有效地解决 Express 应用中嵌套异步数据查询导致的数据缺失问题。这种方法不仅保证了所有数据都能在响应发送前被正确填充,还大大提高了代码的可读性和维护性,使其更符合现代 JavaScript 异步编程的最佳实践。在处理复杂的数据库查询和数据转换时,务必仔细规划异步流程,确保每个环节的数据都已准备就绪。
以上就是Express 中嵌套异步数据查询并正确响应 JSON的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号