JavaScript Promise function returning too early

Viewed 978

I'm executing a bunch of promise functions and then returning the resolved arrays. However, upon returning the arrays, it returns undefined. If i use setInterval to execute after a 3 second, the arrays return values.

Here's the code:

function getData() {
    var a = new Promise((resolve, reject) => {
        getDressData().then(function(result) {
            resolve(result);
            console.log('promise a resolved');
        });
    });

    var b = a.then(function(result) {
        var dressOwners = result.dressOwners;
        return new Promise((resolve, reject) => {
            getOwnerData(dressOwners).then(function(result) {
                resolve(result);
                console.log('promise b resolved');
            });
        });
    });

    return Promise.all([a, b])
    .then(function([resultA, resultB]) {
        if (resultA) {
            if (resultB) {
                console.log('promise all resolved');
                return resultA;
            }
        }
    });
}

To return the arrays:

function getDreses() {
    getData().then(function(result) {
        console.log('Returning promise...');
        console.log(result.dresses[1].colour);
    });
}

Result:

promise a resolved - main.js:679
promise b resolved - main.js:688
promise all resolved - main.js:697
Returning promise... - main.js:706
Uncaught (in promise) TypeError: Cannot read property 'colour' of undefined at main.js:707


However, if I set a 3 second delay before getting any values from the array, it works!

function getDreses() {
    getData().then(function(result) {
        var t = setInterval(() => {
            dresses = result.dresses;
            console.log(dresses.length);
            console.log(dresses[1].colour);
            clearInterval(t);
        }, 3000);
    });
}

Result:

promise a resolved - main.js:679
promise b resolved - main.js:688
promise all resolved - main.js:697
Returning promise... - main.js:706
4 - main.js:711
Blue - main.js:712



So, how would i go about waiting until the arrays have values instead of waiting on a 3 second delay?



Edit (Could be a problem with getDressData() resolving too quickly):

function getDressData() {
    return new Promise((resolve, reject) => {
        var db = firebase.firestore();
        var dresses = [];
        var filters = [];
        var dressOwners = [];
        
        db.collection('dresses').orderBy("date", "desc")
        .get().then(function(doc) {       
            doc.docs.forEach(function(doc) {
                var item = doc.data();
                dresses.push({
                    'dressId' : doc.id,
                    'colour' : item.colour,
                    'credit' : item.credit,
                    'date' : item.date,
                    'description' : item.description,
                    'images' : item.images,
                    'owner' : item.owner,
                    'size' : item.size
                });

                if(filters.filter(x => x.size == item.size).length == 0) {
                    filters.push({"size" : item.size, "colours": [item.colour]});
                } 
                else {
                    var index = filters.findIndex((x => x.size == item.size));
                    
                    if (!filters[index].colours.filter(x => x.colours == item.colour).length > 0) {
                        filters[index].colours.push(item.colour);
                    }
                }

                if (dressOwners.indexOf(item.owner) === -1) {
                    dressOwners.push(item.owner);
                }
            });
        });

        resolve({
            "dresses" : dresses,
            "filters" : filters,
            "dressOwners" : dressOwners
        });
    });
}

1 Answers

Exact this snippet is wrong. (See comments in code)

function getDressData() {
    return new Promise((resolve, reject) => {
        var db = firebase.firestore();
        var dresses = [];
        var filters = [];
        var dressOwners = [];
        

        /*
         * this is a promise from your db
         */
        db.collection('dresses').orderBy("date", "desc")
        .get().then(function(doc) {       
            doc.docs.forEach(function(doc) {
                var item = doc.data();
                dresses.push({
                    'dressId' : doc.id,
                    'colour' : item.colour,
                    'credit' : item.credit,
                    'date' : item.date,
                    'description' : item.description,
                    'images' : item.images,
                    'owner' : item.owner,
                    'size' : item.size
                });

                if(filters.filter(x => x.size == item.size).length == 0) {
                    filters.push({"size" : item.size, "colours": [item.colour]});
                } 
                else {
                    var index = filters.findIndex((x => x.size == item.size));
                    
                    if (!filters[index].colours.filter(x => x.colours == item.colour).length > 0) {
                        filters[index].colours.push(item.colour);
                    }
                }

                if (dressOwners.indexOf(item.owner) === -1) {
                    dressOwners.push(item.owner);
                }
            });
        });

        /*
         * and you're resolving this function outside the db promise.
         * this will resolve before the db-promise finished
         */
        resolve({
            "dresses" : dresses,
            "filters" : filters,
            "dressOwners" : dressOwners
        });
    });
}

you could try to use async/await maybe its better to understand. like so:

// with async, this function will return a promise
// just use it like a promise: getDressData().then(/*...*/)

async function getDressData() {
        var db = firebase.firestore();            

        return await db.collection('dresses').orderBy("date", "desc")
        .get().then(function(doc) {  
            
            // process your db-response

            var dresses = [];
            var filters = [];
            var dressOwners = [];

            doc.docs.forEach(function(doc) {
                var item = doc.data();
                dresses.push({
                    'dressId' : doc.id,
                    'colour' : item.colour,
                    'credit' : item.credit,
                    'date' : item.date,
                    'description' : item.description,
                    'images' : item.images,
                    'owner' : item.owner,
                    'size' : item.size
                });

                if(filters.filter(x => x.size == item.size).length == 0) {
                    filters.push({"size" : item.size, "colours": [item.colour]});
                } 
                else {
                    var index = filters.findIndex((x => x.size == item.size));
                    
                    if (!filters[index].colours.filter(x => x.colours == item.colour).length > 0) {
                        filters[index].colours.push(item.colour);
                    }
                }

                if (dressOwners.indexOf(item.owner) === -1) {
                    dressOwners.push(item.owner);
                }

                return {
                   dresses,
                   filters,
                   dressOwners
                }
            });
        });
}

Related