Nodejs 请教for循环中查询数据库时,只能得到最后一次查询结果?
Nodejs 请教for循环中查询数据库时,只能得到最后一次查询结果?
我express框架做blog的过程中遇到个很纠结的问题,我的mongo数据库里有两张表,
一个是存储用户信息的user表,字段是_id,email,name,password等…
一个是存储文章的blog表,字段是_id,title,content,author_id等…
blog表里的每篇文章除了标题内容等之外还有个命名为author_id的字段,存储文章作者id。
我现在想循环blog表,读取所有文章,找到每篇文章的author_id,
然后根据该author_id读取user表里的name字段,该字段表示作者名称。
但是在循环过程中发现读不到每个for循环时的序列值,只能拿到最后for结束后的序列值。
这是因为数据库查询是异步的,采用回调的方式处理。
这种情况下,我怎么拿到每次for循环的值呢?下面是代码:
module.exports = function(req, res) { var blog = require(’…/models/blog’); var user = require(’…/models/user’); var session_user = req.session.user; var condition = session_user? {author_id: session_user._id} : null;
blog.get(null, function(status, message) {
for (var i=0, count=message.length; i<count; i++) {
var label_arr = message[i].label.split(',');
var j = 0;
var num = label_arr.length;
var new_arr = [];
for (; j<num; j++) {
var k = 0;
var label_arr2 = label_arr[j].split(',');
var len = label_arr2.length;
for (; k<len; k++) {
var clean_elem = label_arr2[k].replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g,'');
if (clean_elem != '') {
new_arr.push(clean_elem);
}
}
}
message[i].label = new_arr;
user.get({_id: message[i].author_id}, function(status, user_message) {
//这个回调函数只能被调用一次,根本拿不到对应用户的name值,我咧个去,纠结死了。。。
message[i].author_name = user_message[0].name; //这里出错,i的值永远是count
blog.get(condition, function(status, self_message) {
res.render('index', {
title: 'NodeJs学习站',
type: 'index',
loginer: session_user,
blog: message,
self_blog: self_message
});
});
});
}
});
};
这个问题的核心在于 Node.js 的异步特性。在你的 for
循环中,每个数据库查询都是异步执行的,这意味着 for
循环不会等待查询完成就继续执行下一次迭代。因此,在回调函数中访问 i
时,循环可能已经结束了,导致你总是获取到最终的 i
值。
为了解决这个问题,可以使用闭包来保存当前循环中的 i
值。以下是一个示例代码:
module.exports = function(req, res) {
var blog = require('../models/blog');
var user = require('../models/user');
var session_user = req.session.user;
var condition = session_user ? { author_id: session_user._id } : null;
blog.get(null, function(status, message) {
var promises = [];
for (var i = 0, count = message.length; i < count; i++) {
(function(index) {
var label_arr = message[index].label.split(',');
var j = 0;
var num = label_arr.length;
var new_arr = [];
for (; j < num; j++) {
var k = 0;
var label_arr2 = label_arr[j].split(',');
var len = label_arr2.length;
for (; k < len; k++) {
var clean_elem = label_arr2[k].replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, '');
if (clean_elem !== '') {
new_arr.push(clean_elem);
}
}
}
message[index].label = new_arr;
// 使用 Promise 解决异步问题
promises.push(new Promise((resolve, reject) => {
user.get({ _id: message[index].author_id }, function(status, user_message) {
if (status === 200 && user_message.length > 0) {
message[index].author_name = user_message[0].name;
resolve();
} else {
reject(new Error("User not found"));
}
});
}));
})(i);
}
// 等待所有 Promise 完成
Promise.all(promises).then(() => {
blog.get(condition, function(status, self_message) {
res.render('index', {
title: 'NodeJs学习站',
type: 'index',
loginer: session_user,
blog: message,
self_blog: self_message
});
});
}).catch(err => {
console.error(err);
res.status(500).send("Internal Server Error");
});
});
};
在这个解决方案中,我们使用了一个立即执行函数表达式(IIFE)来创建一个闭包,从而保存当前循环中的 i
值。这样,每个异步操作都能正确地访问到对应的 i
值。
此外,我们使用 Promise
来管理异步操作,通过 Promise.all
确保所有异步操作完成后才渲染页面。这样可以确保在渲染页面之前,所有的数据库查询都已经完成,并且能够正确地获取到每个文章的作者信息。
你这个javascript绑定的变量发生变化的问题 用下面这种方式解决,一定要用下面这个方式绑定一下变量。
function (i){ do(i); }(i变量绑定)
多看看javascript基础吧,js的坑还是很多的,除了这个,比较有名的还有this关键字,建议看看javascript高级程序设计。
你这种方法,我已经试过了,是不行的。
是的,这是更好的方式
你这种闭包写法也要分场合的,不是什么地方都能用啊
谢谢,我试下forEach。
这代码看的好累,你在异步循环里面调用 res.render?
是的,因为res.reder需要异步请求的数据。
这个问题主要是由于 JavaScript 的异步特性导致的。在 for
循环中执行异步操作(如数据库查询)时,循环变量 i
在异步回调执行时已经完成了所有迭代。因此,回调中的 i
始终为循环完成后的值。
为了确保在异步操作中能够获取到每次循环时正确的 i
值,可以使用 IIFE(立即执行函数表达式)来捕获当前的 i
值。以下是修改后的代码示例:
module.exports = function(req, res) {
var blog = require('../models/blog');
var user = require('../models/user');
var session_user = req.session.user;
var condition = session_user ? { author_id: session_user._id } : null;
blog.get(null, function(status, message) {
message.forEach(function(item) {
(function(i) {
user.get({ _id: item.author_id }, function(status, user_message) {
if (user_message && user_message.length > 0) {
item.author_name = user_message[0].name;
}
blog.get(condition, function(status, self_message) {
res.render('index', {
title: 'NodeJs学习站',
type: 'index',
loginer: session_user,
blog: message,
self_blog: self_message
});
});
});
})(message.indexOf(item));
});
});
};
在这个示例中,我们使用 forEach
方法遍历 message
数组,并通过 IIFE 将当前的 i
值传递给回调函数,从而确保在异步回调中可以访问到正确的索引值。这样,每个异步操作都可以正确地获取到相应的数据。