-1

I know this has been asking thousands of times, but I still haven't been able to find an answer. I have a forEach loop that I am using to loop through a list of users and retrieving specific location data for each. Inside the forEach the first function call gets the branch then the second gets information on that branch.

The problem is that the forEach loop is not completing prior to returning data from the function, which is causing all sorts of issues.

Here is my code:

//getAllUserData() - this is the function which returns prematurely
const getAllUserData = async () => {

    const sequelize = DB.GetDB('mcaintranet');
    const userDB = MCAIntranet.initModels(sequelize);

    userDB.tblUsers.belongsTo(userDB.tblSMSSent, { foreignKey: 'collector', targetKey: 'user' });
    userDB.tblSMSSent.hasMany(userDB.tblUsers);

    let users = await userDB.tblUsers.findAll({
        attributes: ['collector', 'id'],
        include: {
            model: userDB.tblSMSSent,
            attributes: [[sequelize.fn('COUNT', 'tblSMSSent.id'), 'numMessages']]
        },
        raw: true,
        group: ['collector', 'tblUsers.id'],
    })

    //return list of all users and texts sent
    //UserName, User Province Code, # Messages Sent, user  Number

    try {
        await users.forEach(async user => {
            const branch = await Users.getUserBranch(user);
            const province = getKey(branch);
            user.province = province;
            // console.log("User: ", [user, branch])
            //clean up JSON from join results
            user.numMessages = user["tblSMSSent.numMessages"];
            delete user["tblSMSSent.numMessages"];
        })

        const obj = { 'users': users };
        console.log("DONE: ", obj)
        return obj;
    }
    catch (ex) {
        console.log("ERROR: ", ex)
        const msg = ex.Message;
        return { results: "False", message: "SMS.js 61" + msg }; //is user allowed to use SMS page)
    }
}
//getUserBranch() - accessor function to return branch number
const getUserBranch = async (user) => {
    // console.log("Branch User: ", user)
    let branch = getUserProps(user.collector ? user.collector : user, 'branch');
    // console.log(props)
    if (!branch) branch = 0;
    return branch;
}
//getUserProps - pulls all user properties from database and filters as necessary
const getUserProps = async (user, propName = null) => {
    let ret = {};

    if (user.collector) user = user.collector;
    user = user.trim();
    let filter = {
        collector: user
    }
    if (propName) {
        filter.propertyName = propName
    }

    // console.log("Filter: ", filter)
    const sequelize = DB.GetDB('MCAIntranet');
    const propsDB = MCAIntranet.initModels(sequelize);

    //get all values contained for user
    let props = await propsDB.tblUserProperties.findAll({
        attributes: ['id', 'propertyName', 'propertyValue', 'updated'],
        where: filter,
        order: [['updated', 'DESC']],
    })

    // console.log("Props: ", props);

    let distinctProps = await propsDB.tblUserProperties.findAll({
        attributes: ['propertyName'],
        DISTINCT: true,
        where: filter,
    })

    //the SMS Credits item is sometimes added as a second row
    //so loop through all potential value rows for user
    distinctProps.forEach(dr => {
        // console.log(dr);
        var name = dr.propertyName.toLowerCase();
        // console.log("name: ", name)
        try {
            //get individual values for each property user possesses
            let tmpRows = props.filter(row => row.propertyName.toLowerCase() == name);
            // console.log("tmpRows: ", tmpRows)

            //get the most recent value
            let row = tmpRows[tmpRows.length - 1];
            var item = row.propertyName.trim();
            var value = row.propertyValue.trim();
            //add to returned JSON
            ret[item] = value;
        } catch (ex) {
            console.log("USERS.js 157 " + ex.message);
            return ({ error: "USERS.js 158" + ex.Message });
        }
    })
    // console.log("Ret: ", ret)
    return ret;
}
//getKey() - returns necessary data about the user province
const getKey = (data) => {
    let branch;
    try {
        branch = data.branch ? data.branch : data;
    }
    catch (ex) {
        console.log(data)
        console.log(ex)
    }
    branch = parseInt(branch);
    // console.log(branch)
    try {
        let acctKey = "";
        let province = "";
        let abbr = "";
        // console.log("Branch: ", branch)
        switch (branch) {
            case 4: //vancouver
            case '4':
                abbr = "BC";
                acctKey = key["BC"];
                province = "British Columbia";
                break;
            case 7: //Montreal
            case '7':
                abbr = "QC";
                acctKey = key["QC"];
                province = "Quebec";
                break;
            case 9: //Toronto
            case '9':
                abbr = "ON";
                acctKey = key["ON"];
                province = "Ontario";
                break;
            default: //Edmonton
                abbr = "AB";
                acctKey = key["AB"];
                province = "Alberta";
                break;
        }
        return { key: acctKey, province: province, abbr: abbr };
    }
    catch (ex) {
        throw ex;
    }
}

Somewhere in this conglomeration of awaits and asyncs something is lacking causing the getAllData() function to return user names without the location data. How can I block execution until I have all the data? Please ignore the extraneous awaits I have put in while endeavoring to solve this problem.

Thanks.

Geoff
  • 353
  • 3
  • 19
  • Yes, this has been asked a thousand times before. Did you find any of these questions? Did you attempt to use the approach(es) from their answers? – Bergi Sep 14 '21 at 19:36

1 Answers1

1

Instead of forEach, you want this:

await Promise.all(users.map(async (user) => {...}))

map returns the promise for each inner operation, and await-ing Promise.all waits until all of those promises are finished

Adam Jenkins
  • 51,445
  • 11
  • 72
  • 100