[Solved] Help with the order of execution: async.each in an async.waterfall



  • Hello,

    I am trying to use an async.each during an asyc.waterfall in a NodeBB plugin, however the order of execution is not what I expect.

    Code:

    async.waterfall([
        function(next) {
            Groups.getGroupsFromSet('groups:createtime', 0, 0, -1, next);
        },
        function(groups, next) {
            // log(`groups: ${groups}`)
            // log(`groups: ${groups[0].name}`)
            next(err, groups)
        },
        function(groups, next) {
            async.each(groups, function(element, eachCallback) {
                if (!element.name.match('privileges')) {
                    Groups.ownership.isOwner(uid, element.name, function(err, isOwnerBool) {
                        if (isOwnerBool) {
                            renderData.groups.push(element)
                            log(`element.name: ${element.name}`)
                        }
                    })
                }
                eachCallback(err)
            },
            function(err) {
                if (err) {
                    log(`ERROR in async.each! ${err}`);
                } else {
                    log('No error happened in any steps, next()!');
                }
                next(err)
            })
        },
        function(next) {
            log('I got called!')
            log(`renderData.groups: ${renderData.groups}`)
            next(err)
        }
    ],
    function(err) {
        if (err) {
            log(`ERROR! ${err}`);
        } else {
            log('No error happened in any steps, operation done!');
        }
    });
    

    Actual output:

    No error happened in any steps, next()!
    I got called!
    renderData.groups:
    No error happened in any steps, operation done!
    element.name: My Group A
    element.name: My Group B
    

    Expected output:

    element.name: My Group A
    element.name: My Group B
    No error happened in any steps, next()!
    I got called!
    renderData.groups: [object Object],[object Object]
    No error happened in any steps, operation done!
    

    Any help is appreciated, thank you 🙂

    Edit: Adjusted expected output, sorry for the confusion.


  • Global Moderator

    You need to call eachCallback within the callback you pass to Groups.ownership.isOwner. In the case you have a privilege group, you can just exit early like so:

    // not exactly what you want, just an example
    if (condition) {
      callback();
      return;
    }
    
    // then do other stuff
    


  • Do you mean this?

    async.waterfall([
        function(next) {
            Groups.getGroupsFromSet('groups:createtime', 0, 0, -1, next);
        },
        function(groups, next) {
            // log(`groups: ${groups}`)
            // log(`groups: ${groups[0].name}`)
            next(err, groups)
        },
        function(groups, next) {
            async.each(groups, function(element, eachCallback) {
                if (!element.name.match('privileges')) {
                    Groups.ownership.isOwner(uid, element.name, function(err, isOwnerBool) {
                        if (isOwnerBool) {
                            renderData.groups.push(element)
                            log(`element.name: ${element.name}`)
                        }
                        eachCallback(err)
                    })
                }
            },
            function(err) {
                if (err) {
                    log(`ERROR in async.each! ${err}`);
                } else {
                    log('No error happened in any steps, next()!');
                }
                next(err)
            })
        },
        function(next) {
            log('I got called!')
            log(`renderData.groups: ${renderData.groups}`)
            next(err)
        }
    ],
    function(err) {
        if (err) {
            log(`ERROR! ${err}`);
        } else {
            log('No error happened in any steps, operation done!');
        }
    });
    

    This leads to the following output:

    element.name: My Group A
    element.name: My Group B
    

    For context, I am trying to get a list of all groups, of which the current user is the owner.Therefore I also do not want to exit the async.each on the first privilege group, but find them all and add them to the renderData.groups array.


  • Global Moderator

    @navid-sassan you need to call eachCallback no matter what, otherwise it will hang.

    async.each(groups, function(element, eachCallback) { 
      if (element.name.match('privileges')) {
        eachCallback();
        return; // only exits this callback function, skips the ownership check
      }
      Groups.ownership.isOwner(uid, element.name, function(err, isOwnerBool) {
        if (isOwnerBool) {
          renderData.groups.push(element) 
          log(`element.name: ${element.name}`)
        }
        eachCallback(err)
      }),
    }
    

  • Global Moderator

    Just FYI, pretty sure you can use async.filter for this.



  • Thank you very much! The code worked, and I will look into async.filter, it looks promising, thanks for the hint 😄


Log in to reply
 

Suggested Topics

| |