Q: Why is my callback function receiving incorrect parameter values? I have a function (called rankCheck), which takes three parameters:


*

*Guild object (aka a Discord server)

*UserId

*Callback Function


The function will fetch the last 500 messages from every text channel in the provided guild. It will then will then only keep any messages that start with "!rank" and were sent by the provided UserId. Finally, it will count the remaining messages and pass the integer to the callback function.
async function rankChecks(guild, userId = *REMOVED FOR PRIVACY*, callback){
    sumOfRankChecks = 0;       
    guild.channels.cache.each(channel => { //for each text channel, get # of rank checks for userId in last 500 msgs.
        if (channel.type === "text"){
            fetchMessages(channel, 500).then(msgs => {
                let filteredMsgs = msgs.filter(msg => msg.content.startsWith("!rank") && msg.member.user.id == userId);
                sumOfRankChecks = sumOfRankChecks + filteredMsgs.length;
            });
        }
    });
    callback(sumOfRankChecks);
}


Since discord only allows fetching 100 messages at once, I use this function (fetchMessages) to bypass this limit, by sending multiple requests, and then combining the results into one.
async function fetchMessages(channel, limit) {
    const sum_messages = [];
    let last_id;

    while (true) {
        const options = { limit: 100 };
        if (last_id) {
            options.before = last_id;
        }

        const messages = await channel.messages.fetch(options);
        sum_messages.push(...messages.array());
        last_id = messages.last().id;

        if (messages.size != 100 || sum_messages >= limit) {
            break;
        }
    }

    return sum_messages;
}

When I call the rankCheck function, the return value is always 0
        rankChecks(msg.guild, *REMOVED FOR PRIVACY*, function(int){
            console.log(int);
        });

Output:
0

However when I add a console.log into my rankCheck function:
async function rankChecks(guild, userId = *REMOVED FOR PRIVACY*, callback){
    sumOfRankChecks = 0;       
    guild.channels.cache.each(channel => { //for each text channel, get # of rank checks for userId in last 500 msgs.
        if (channel.type === "text"){
            fetchMessages(channel, 500).then(msgs => {
                let filteredMsgs = msgs.filter(msg => msg.content.startsWith("!rank") && msg.member.user.id == userId);
                sumOfRankChecks = sumOfRankChecks + filteredMsgs.length;
                console.log(sumOfRankChecks) //NEW CONSOLE.LOG!!!!!!!!!!!!!!!
            });
        }
    });
    callback(sumOfRankChecks);
}

Output:
3
5

This is the output I was expecting. Since I have 2 text channels in my server, I got 2 logs. If you had 3 channels, you would get 3 logs, etc. 3 messages from channel #1, and 2 messages from channel #2, therefore in total, there are 5 messages.
5 should be the integer that is passed into the callback function, but 0 is passed instead. Why is this?

A: Your callback function is being called before you even change sumOfRankChecks. Collection#each (and Map#forEach() and the gang) cannot wait for Promises to resolve because of the way they're built. Your code also wouldn't wait anyway, because you're not using await.
Despite what one might think is happening, guild.channels.each() is called, and callback() is called immediately after. This is the source of your confusion.
For more about async vs sync, you can check out the explanation in my answer here. You must use a for loop and await, or refactor your code so that async/await syntax is not necessary.

NOTE: The Discord.js documentation hyperlinked is for recently released v12. If your Discord.js isn't up to date, switch to the correct version at the top of the page for accurate info.
