简体   繁体   中英

Undefined errors using Node.js, Mongoose, and Discord.js [Cannot read property of undefined]

I've been scouring similar problems but haven't seem to have found a solution that quite works on my end. So I'm working on a Discord bot that takes data from a MongoDB database and displays said data in the form of a discord embedded message using Mongoose. For the most part, everything is working fine, however one little section of my code is giving me trouble. So I need to import an array of both all available users and the "time" data of each of those users. Here is the block of code I use to import said data:

for (i = 0;i < totalObj; i++){
    timeArray[i] = await getData('time', i);
    userArray[i] = await getData('user', i);
}

Now this for loop references a function I made called getData which obtains the data from MongoDB by this method:

async function getData(field, value){
var data;
await stats.find({}, function(err, result){
    if(err){
        result.send(err);
    }else{
        data = result[value];
    }
});

if(field == "user"){
    return data.user;
}else if (field == "time"){
    return data.time;
}else{
    return 0;
}

So that for loop is where my errors currently lie. When I try to run this code and display my data through a discord message, I get this error and the message does not get sent:

(node:13936) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'time' of undefined

Now the strange thing is, this error does not happen every time. If I continue calling the command that triggers this code from my discord server, it's almost like a 50/50 shot if the command actually shows the message or instead gives this error. It is very inconsistent.

This error is confounding me, as the undefined part does not make sense to me. The objects that are being searched for in the mongoDB collection are definitely defined, and the for loop never exceeds the number of objects present. My only conclusion is that I'm doing something wrong with my asynchronous function design. I have tried altering code to use the getData function less often, or to not use awaits or asynchronous design at all, however this leaves my final discord message with several undefined variables and an eventual crash.

If anyone has any advice or suggestions, that would be very much appreciated. Just for reference, here is the full function that receives the data, sorts it, and prepares a string to be displayed on the discord server (though the error only seems to occur in the first for loop):

async function buildString(){
var string = "";
var totalObj;
var timeArray = [];
var userArray = [];
var stopSort = false;

await stats.find({}, function(err, result){
    if(err){
        result.send(err);
    }else{
        totalObj = result.length;
    }
});

for (i = 0;i < totalObj; i++){
    timeArray[i] = await getData('time', i);
    userArray[i] = await getData('user', i);
}

while(!stopSort){
    var keepSorting = false;
    for(i = 0; i < totalObj ; i++){
        var target = await convertTime(timeArray[i]);
        for(j = i + 1 ; j < totalObj ; j++){
            var comparison = await convertTime(timeArray[j]);
            if(target > comparison){

                //Switch target time with comparison time so that the lower time is up front
                var temp = timeArray[i];
                timeArray[i] = timeArray[j];
                timeArray[j] = temp;

                //Then switch the users around so that the user always corresponds with their time
                var userTemp = userArray[i];
                userArray[i] = userArray[j];
                userArray[j] = userTemp;

                //The loop will continue if even a single switch is made
                keepSorting = true;
            }
        }
    }

    if(!keepSorting){
        stopSort = true;
    }
}

//String building starts here
var placeArray = [':first_place: **1st', ':second_place: **2nd', ':third_place: **3rd', '**4th', '**5th', '**6th', '**7th', '**8th', '**9th', '**10th'];
for(i = 0; i < totalObj; i++){
    string = await string.concat(placeArray[i] + ": " + userArray[i] + "** - " + timeArray[i] + " \n\n");
    console.log('butt');
}


console.log("This String:" + string);
return string;

}

I think problem is you are trying to await function with callback, it will not work => access to data.time may run before data = result[value] . If you need await callback, you can use custom Promise (or use util.promisify, more info here )

Promise:

function findStats(options) {
        return new Promise((resolve, reject) => {
            return stats.find(options, function (err, result) {
                if (err) {
                    return reject(err)
                }
                return resolve(result)
            })
        })
    }

utils.promisify

const util = require('util');
const findStats = util.promisify(stats.find);

Now you can use await in your function

async function getData(field, value) {
    try {
        const result = await findStats({})
        const data = result.value

        if (field === 'user') {
            return data.user
        }
        if (field === 'time') {
            return data.time
        }
        return 0
    } catch (error) {
        // here process error the way you like
        // or remove try-catch block and sanitize error in your wrap function
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM