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)) }) } }) } } 但是 这种方式 感觉 存在问题 ,请问应该如何实现??


19 回复

对于你提到的问题,确实存在异步处理的难题。在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();

解释

  1. 异步函数:我们定义了一个名为processUsers的异步函数。使用async关键字可以让我们在函数体内使用await关键字。
  2. Promise包装:我们将每个数据库查询包装成一个Promise,这样就可以使用await来等待这些查询的结果。
  3. 循环处理:在获取到userCollection后,我们通过一个简单的for...of循环遍历每个用户。同样地,我们也对roleCollectionactionCollection使用了这种方式。
  4. 错误处理:在整个过程中,我们使用了try...catch来捕获任何可能发生的错误,并在控制台输出错误信息。
  5. 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的方式。。。

也可以试试 promise,也可以把移步调用的部分抽离出来放到独立的 function 里面,确保回调执行时外部闭包的状态是正确的

在你的场景中,由于涉及到多次异步查询,直接使用嵌套的 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.");
        }
    }); // 外层用户循环结束时调用此回调
});

解释

  1. 引入 asyncasync 库提供了一系列实用的异步操作函数。
  2. 用户查询:首先查询所有用户。
  3. 使用 eachOfSeries:对于每一个用户,执行一系列操作。eachOfSeries 确保每个用户的操作按顺序执行,不会并行。
  4. 角色查询:对于每一个用户,再查询其所有角色。
  5. 使用 eachOfSeries:对每一个角色,执行一系列操作。
  6. 动作查询与 Redis 设置:对于每一个角色,查询其所有动作,并将结果存储到 Redis 中。
  7. 错误处理:如果在任何步骤中发生错误,会立即停止并传递给回调函数。

这样可以避免回调地狱,并确保操作按顺序执行。

回到顶部