for 循环 异步处理 Nodejs 求助
for 循环 异步处理 Nodejs 求助
根据条件查询数据库,得到一个数据集合,然后再根据数据集合进行循环查询。。形如如下格式:
users.find({userId:""},function(err,userCollection){// for(var i=0 ;i<userCollection.length;i++){ roles.find({roleId:userCollection[i].roleId},function(err,roleCollection){// for(var j =0 ;j<roleCollection.length;j++){ actions.find({actionId:roleCollection[j].actionId},function(err,actionCollection){// redis.set(userCollection[i].id,JSON.stringify(actionCollection)) }) } }) } } 但是 这种方式 感觉 存在问题 ,请问应该如何实现??
对于你提到的问题,确实存在异步处理的难题。在Node.js中,当你在一个循环内部嵌套异步操作时,可能会遇到一些常见的问题,比如回调地狱(Callback Hell)或者无法正确地等待所有异步操作完成。
解决方案
我们可以使用现代的JavaScript特性,如async/await
,来简化异步逻辑并确保所有的查询都完成后才继续执行后续的操作。以下是一个改进后的代码示例:
const users = require('./models/users'); // 假设这是你的用户模型
const roles = require('./models/roles'); // 假设这是你的角色模型
const actions = require('./models/actions'); // 假设这是你的动作模型
const redis = require('redis').createClient(); // 假设这是你的Redis客户端
// 定义一个异步函数
async function processUsers() {
try {
const userCollection = await new Promise((resolve, reject) => {
users.find({}, (err, collection) => {
if (err) return reject(err);
resolve(collection);
});
});
for (let user of userCollection) {
const roleCollection = await new Promise((resolve, reject) => {
roles.find({ roleId: user.roleId }, (err, collection) => {
if (err) return reject(err);
resolve(collection);
});
});
for (let role of roleCollection) {
const actionCollection = await new Promise((resolve, reject) => {
actions.find({ actionId: role.actionId }, (err, collection) => {
if (err) return reject(err);
resolve(collection);
});
});
// 将结果存储到Redis
redis.set(user.id, JSON.stringify(actionCollection), (err) => {
if (err) console.error('Error storing in Redis:', err);
});
}
}
console.log('All operations completed successfully.');
} catch (error) {
console.error('An error occurred:', error);
}
}
processUsers();
解释
- 异步函数:我们定义了一个名为
processUsers
的异步函数。使用async
关键字可以让我们在函数体内使用await
关键字。 - Promise包装:我们将每个数据库查询包装成一个Promise,这样就可以使用
await
来等待这些查询的结果。 - 循环处理:在获取到
userCollection
后,我们通过一个简单的for...of
循环遍历每个用户。同样地,我们也对roleCollection
和actionCollection
使用了这种方式。 - 错误处理:在整个过程中,我们使用了
try...catch
来捕获任何可能发生的错误,并在控制台输出错误信息。 - Redis存储:最后,我们将结果存储到Redis中。如果存储过程中发生错误,我们会将其记录下来。
这种方法避免了回调地狱的问题,并且能够更清晰地管理异步操作。希望这能帮助你解决问题!
樓上要實現一個什麼效果 從你的描述看,可能的問題就是執行玩循環後 循環內部的一些回調都還沒有調用
异步的循环要自己保证好状态的,这样子肯定是不行的
可以使用async之类的库
我的 目的 是要 根据 用户 查找其 用户角色 再根据用户角色 查找对应的角色权限 ,但是由于用户 角色 权限 她们之间是多对多的关系 所以需要循环查询。。最终的目的 是获取用户具有哪些权限 ,但是由于是异步处理,上述方案感觉会存在问题(没实验验证),首先请问上述方案存在 问题吗 ,其次是有更好的实现方式吗 ?
请问 都是采用同步方式获取数据吗?
我以前遇到的问题和你类似:比如有2张表,user 和 posts,从posts查询完得到一个数组之后要到user获取相应的用户信息。开始我也是像你那样写的,但是发现不行,后来我就用了node自带的 event 模块,注册事件并监听就ok了。
看看朴灵的 EventProxy 地址:https://github.com/JacksonTian/eventproxy 这个应该用异步编程的发布/订阅模式,朴灵的EventProxy很好用的,我们这个中文社区就用的EventProxy
现准备尝试async 中waterfall 方法 ,如不行再使用event模块。。。
我一开始也是想到朴灵的 EventProxy 但是从网上的资料看 ,没发现有参数传递的,请问它具有参数传递的功能吗?
这是node.js中比较著名的一个问题了,主要是异步和闭包相关的,可以这样写 users.find({userId:""},function(err,userCollection) {// for (var i = 0; i < userCollection.length; i++) { (function (idx) { roles.find({roleId: userCollection[idx].roleId}, function (err, roleCollection) {// for (var j = 0; j < roleCollection.length; j++) { actions.find({actionId: roleCollection[j].actionId}, function (err, actionCollection) {// redis.set(userCollection[idx].id, JSON.stringify(actionCollection)) }) } }) })(i); } });
用一个自执行的function把外层for循环里面的东西包起来,把i当做参数传给function,那idx其实就是i,这样的话idx的值就可以保存下来并被里面的操作正确的访问到了。否则因为异步的原因,你里面的操作每次取到i的值是最后一次i的值。理解了闭包和异步对于学习node进步会很明显,楼主加油。
楼主 你应该再学学MongoDB。 MongoDB里就有抓取对应的模型的方法。 比如 与一条用户信息(userInfo)对应有N个角色信息(roles),可以使用某个字段将两种模型关联起来,就是$ref 属性 比如 userInfo.admin={$ref:‘roles’,$id:【roles集合内某文档的_id的值】}。 在查询到这条信息时,你可以使用这个属性抓取对应的role文档. 这里只是一个提示,具体的操作方法 请楼主自己搜索!
其实,本人使用的是mongoosejs在对付此类问题时就很简单。楼主可以看一下这个。 http://mongoosejs.com/docs/populate.html 如果你看了这个,楼主 你会豁然开朗。
嗯 說的對 沒有注意他最內層還有用到i 樓主都套了三層 還是用點async什麼的吧
其实 我本来也想使用此方法的,但是出现如下问题: http://cnodejs.org/topic/538568fea087f45620d07847 由于是初学者,实在找不到原因,所以就放弃了。。。望解答,万分感谢!!
由于是初学者,对于闭包还不怎么了理解,正在学习中,感谢提供解决方案!
var userCollection = yield users.find({userId:""})
for(var i=0 ;i<userCollection.length;i++){
var roleCollection = yield roles.find({roleId:userCollection[i].roleId});
for(var j =0 ;j<roleCollection.length;j++){
var actionCollection = yield actions.find({actionId:roleCollection[j].actionId});
redis.set(userCollection[i].id,JSON.stringify(actionCollection))
}
}
学习了,第一次看到使用yield的方式。。。
在你的场景中,由于涉及到多次异步查询,直接使用嵌套的 for
循环会导致回调地狱(callback hell),使得代码难以维护。你可以使用 async
库或者 Promise
来解决这个问题。
以下是使用 async
库中的 eachOfSeries
方法来实现的示例:
const async = require('async');
users.find({ userId: "" }, function (err, userCollection) {
if (err) throw err;
async.eachOfSeries(userCollection, (user, idx, callback) => {
roles.find({ roleId: user.roleId }, function (err, roleCollection) {
if (err) return callback(err);
async.eachOfSeries(roleCollection, (role, idx, callback) => {
actions.find({ actionId: role.actionId }, function (err, actionCollection) {
if (err) return callback(err);
redis.set(user.id, JSON.stringify(actionCollection), callback);
});
}, callback); // 外层角色循环结束时调用此回调
});
}, (err) => {
if (err) {
console.error("An error occurred:", err);
} else {
console.log("All tasks completed successfully.");
}
}); // 外层用户循环结束时调用此回调
});
解释
- 引入
async
库:async
库提供了一系列实用的异步操作函数。 - 用户查询:首先查询所有用户。
- 使用
eachOfSeries
:对于每一个用户,执行一系列操作。eachOfSeries
确保每个用户的操作按顺序执行,不会并行。 - 角色查询:对于每一个用户,再查询其所有角色。
- 使用
eachOfSeries
:对每一个角色,执行一系列操作。 - 动作查询与 Redis 设置:对于每一个角色,查询其所有动作,并将结果存储到 Redis 中。
- 错误处理:如果在任何步骤中发生错误,会立即停止并传递给回调函数。
这样可以避免回调地狱,并确保操作按顺序执行。