求助,Nodejs操作数据库返回数据问题
求助,Nodejs操作数据库返回数据问题
有个应用场景:首页文章显示,输出所有的文章,每篇文章除了本身的数据外还要带上该文章的作者名,保存文章的时候已经带有作者的id,现在的问题是输出所有文章的时候,首先读取出每篇文章的作者id,再根据这个作者id去用户表里找对应的作者名。但是最后只能得到一个用户的数据,为什么呢?纠结了好多天。。。
module.exports = function(req, res) { var blog = require(’…/models/blog’); var user = require(’…/models/user’);
blog.get(null, function(status, message) { message.forEach(function(m) { user.get({_id: m.author_id}, function(ustatus, umessage) { console.log(umessage); }); }); }); };
当然可以!根据你的描述,你希望在获取所有文章时,能够同时获取每篇文章对应的作者名。你目前的做法是在遍历每篇文章时,通过作者ID去查询用户表,但最终只得到了一个用户的返回结果。这通常是因为异步操作没有正确处理,导致后续的回调函数没有按预期执行。
我们可以使用 async
库来帮助我们管理异步操作,并确保每个查询都能正确完成。下面是改进后的代码示例:
const async = require('async');
module.exports = function(req, res) {
var blog = require('../models/blog');
var user = require('../models/user');
blog.get(null, function(status, messages) {
if (status !== 'success') {
return res.status(500).send({ error: 'Failed to fetch blogs' });
}
const authorPromises = messages.map(message => {
return new Promise((resolve, reject) => {
user.get({ _id: message.author_id }, function(ustatus, umessage) {
if (ustatus === 'success') {
resolve({
blog: message,
author: umessage
});
} else {
reject(new Error('Failed to fetch user'));
}
});
});
});
async.parallel(authorPromises, function(err, results) {
if (err) {
return res.status(500).send({ error: 'Failed to fetch authors' });
}
res.json(results.map(result => ({
...result.blog,
authorName: result.author.name // 假设作者信息中包含 name 字段
})));
});
});
};
解释
- 引入
async
库:async
库提供了parallel
方法,可以帮助我们并行处理多个异步任务。 - 获取文章列表:从
blog
模型中获取所有文章。 - 创建 Promise 数组:对于每篇文章,创建一个新的
Promise
来获取对应的作者信息。 - 使用
async.parallel
并行处理:将这些Promise
传递给async.parallel
,这样可以确保所有请求都完成后再进行下一步处理。 - 处理结果:在所有请求完成后,将每篇文章及其对应的作者信息组合起来,然后发送给客户端。
这样,你就可以确保每个文章都能正确地获取到对应的作者信息,并且不会因为异步操作未正确处理而导致数据丢失。
this has nothing to do with node. more like a javascript understanding problem. it’s because of the function scope and the nature of closure.
you should use async. https://github.com/caolan/async
能解释的清楚点吗,只能用同步来解决了?
javascript closure。 中文不知道叫什么。
var funcs = {}; for (var i = 0; i < 3; i++) { // let’s create 3 functions funcs[i] = function() { // and store them in funcs console.log("My value: " + i); // each should log its value. }; } for (var j = 0; j < 3; j++) { funcsj; // and now let’s run each one to see }
和下一个的区别。
var funcs = [];
function createfunc(i) { return function() { console.log("My value: " + i); }; }
for (var i = 0; i < 3; i++) { funcs[i] = createfunc(i); }
for (var j = 0; j < 3; j++) { funcsj; // and now let’s run each one to see }
我明白这些区别,但是解决不了我的问题呀。。。。
找到了一个变通的方案,把所有的文章查询出来,然后把所有的用户查询出来,在用户数据返回的回调方法中,遍历匹配id值。。。 module.exports = function(req, res) { var blog = require(’…/models/blog’); var user = require(’…/models/user’);
blog.get(null, function(status, message) { user.get(null, function(ustatus, umessage) { message.forEach(function(m) { umessage.forEach(function(um) { if (m.author_id == um._id) { m._author_name = um.name; } }); });
console.log(message); }); }); };
这样,每篇文章中就包含了作者名了,以后扩展用户的其他信息,比如头像,就很方便了。不用在每篇文章中 都预留用户名和用户头像之类的数据了,只要每篇文章中保留一个作者id,就可以通过这个接口拿到该用户的 所有数据了。。。。
no that’s not a good solution. if you have many users or many blogs, this solution will not scale. you cannot bring in all the blocks and all the users into the application. it will either crash the application OR it will take a long time.
why not first call the blog, get all the ids and put it in an array and then call user.get on the ids. instead of doing forEach and do the function call, just aggregate and make the array first.
why not first call the blog, get all the ids and put it in an array and then call user.get on the ids. 你这句话的意思跟我第一次发的代码没区别吧? 不过,第二种方案的性能确实差。。。
make the array and pass the array into user.getAll(array) instead of doing m.author_id
mongoose可以使用path关联查询,请搜
呵呵,我也刚开始看。觉得你代码应该没问题。不过要是真的有问题的话,可以试试用下面的代码
blog.get(null, function(status, message) { message.forEach(function(m) { var theID = {_id: m.author_id}; user.get(theID, function(ustatus, umessage) { console.log(umessage); })(theID); }); });
我觉得你还是先看下message.length是多少吧,先确定是不是大于1!然后在看看每个m.author_id是多少
你的写法是错误的。已经确定message.length>1
我感觉这段代码没问题。
连的什么数据库,不是MongoDB吧?
是的
建议你写一个JS文件,旁人只要安装了MongoDB,执行这个文件就可以重现上述问题。
这可能有助于问题的解决。
是个好办法,等我弄好了,发上来。。
你的问题是由于异步操作导致的数据获取问题。在Node.js中,当你在一个循环中执行异步操作(如数据库查询)时,这些操作实际上是并行执行的,而不是按顺序执行。这意味着当你的 console.log(umessage)
执行时,循环可能已经完成,所以你最终只能看到最后一个用户的查询结果。
为了确保每个文章都能正确地获取到对应的作者名,你需要使用异步处理方法,比如 async/await
或者 Promise.all()
。下面是使用 async/await
的示例代码:
const async = require('async');
module.exports = async function(req, res) {
var blog = require('../models/blog');
var user = require('../models/user');
let blogs = await blog.get(null);
// 使用 Promise.all 来等待所有用户信息获取完毕
let users = await Promise.all(blogs.map(async (m) => {
let umessage = await user.get({_id: m.author_id});
return { ...m, authorName: umessage.name }; // 假设 umessage 有一个 name 字段
}));
res.json(users);
};
这段代码做了以下几件事:
- 首先获取所有的文章。
- 使用
Promise.all
和map
方法来并行获取每个文章的作者信息。 - 将作者名字添加到文章对象中,并返回最终的结果。
这样可以确保你在响应客户端之前,所有文章都已正确地关联上了作者的名字。