Completing loops inside of node.js promises

54 views Asked by At

I am trying to complete a few loops over firebase objects using .forEach and I am also using promises. This isn't working out how I had planned it. My basic problem is that the loops inside of my promises complete well after the promise chain itself completes. Here is my function:

var queue = new Queue(productUpdateQueue, function(data, progress, resolve, reject) {

    var incomingUpdateData = data;
    var receiptID = incomingUpdateData.receiptID;
    var userID = incomingUpdateData.userID;
    var oldProductID = incomingUpdateData.oldProductID;
    var newProductID = incomingUpdateData.newProductID;
    var newReceipt = incomingUpdateData.newReceipt;

    var postID = "";

    var updateObject = {};

    updateObject['usersPrivate/'+userID+'/receipts/'+receiptID+'/items/'+oldProductID] = null;
    updateObject['usersPrivate/'+userID+'/receipts/'+receiptID+'/items/'+newProductID] = newReceipt;

    clicks.child('VigLink').orderByChild('item').equalTo(oldProductID).once('value', function(cuidSnapshot) {
        return cuidSnapshot.forEach(function(cuidSnapshot) {
            var cuid = cuidSnapshot.key;
            updateObject['clicks/VigLink/'+cuid+'/item'] = newProductID;
            console.log('one');
            progress(20);
        });
    }).then(function() {
        return userReceiptMetrics.child(userID).child('receipts').child(receiptID).child('items').child(oldProductID).once('value', function(oldSnapshot) {
            var data = oldSnapshot.val()
            updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/items/'+oldProductID] = null
            updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/items/'+newProductID] = data
            if (data != null) {
                updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/itemIDs/'+newProductID] = now
                updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/itemIDs/'+oldProductID] = null
            };
            console.log('two');
            progress(40);
        });
    }).then(function() {
        return userReceiptMetrics.child(userID).child('shops').child(oldProductID).once('value', function(oldSnapshot) {
            var data = oldSnapshot.val()
            updateObject['userReceiptMetrics/'+userID+'/shops/'+oldProductID] = null;
            updateObject['userReceiptMetrics/'+userID+'/shops/'+newProductID] = data;
            if (data != null) {
                updateObject['userReceiptMetrics/'+userID+'/shopIDs/'+newProductID] = now;
                updateObject['userReceiptMetrics/'+userID+'/shopIDs/'+oldProductID] = null;
            };
            console.log('three');
            progress(60);
        });
    }).then(function() {
        posts.once('value', function(postSnapshot) {
            // use Promise.all and Array#map to wait for all these queries to finish

            var allPosts = postSnapshot.val()
            var postKeys = Object.keys(allPosts)

            return Promise.all(postKeys.map(function(postKey) {
                var postID = postKey;

                return posts.child(postID).child('items').child(oldProductID).once('value', function(itemSnapshot) {

                    return itemSnapshot.forEach(function(itemSnapshot) {
                        var itemData = itemSnapshot.val()
                        console.log('post snapshot'+ itemData);
                        updateObject['posts/'+postID+'/items/'+oldProductID] = null
                        updateObject['posts/'+postID+'/items/'+newProductID] = itemData
                    });
                });
            }));
        });
    }).then(function() {
        // Move to next item
        return console.log('hey look here'+updateObject['posts/'+postID+'/items/'+newProductID]);
        return firebaseRoot.update(updateObject, function(error) {
            if (error) {
                console.log("Error updating data:", error);
                reject()
            } else {
                progress(100);
                // resolve();
                console.log('four');
            }
        });
    }).then(function() {
        // Move to next item
        return console.log('second one'+updateObject['posts/'+postID+'/items/'+newProductID]);
        return firebaseRoot.update(updateObject, function(error) {
            if (error) {
                console.log("Error updating data:", error);
                reject()
            } else {
                progress(100);
                // resolve();
                console.log('four');
            }
        });
    });

    // Finish the task asynchronously
    setTimeout(function() {
        reject();
    }, 10000);
});

And here is the output:

one
two
three
hey look hereundefined
second oneundefined
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]

I think I might be using promises incorrectly but I don't know.

1

There are 1 answers

0
WikipediaBrown On

I figured out that I should be using Promise.all() in order to wait for my forEach loops to complete. Here is the code I used to solve my problem, enjoy:

var queue = new Queue(productUpdateQueue, function(data, progress, resolve, reject) {

    var incomingUpdateData = data;
    var receiptID = incomingUpdateData.receiptID;
    var userID = incomingUpdateData.userID;
    var oldProductID = incomingUpdateData.oldProductID;
    var newProductID = incomingUpdateData.newProductID;
    var newReceipt = incomingUpdateData.newReceipt;

    var postID = "-KZOO0UII67uOmYo6DJh";

    var postKeys = [];

    var updateObject = {};

    updateObject['usersPrivate/'+userID+'/receipts/'+receiptID+'/items/'+oldProductID] = null;
    updateObject['usersPrivate/'+userID+'/receipts/'+receiptID+'/items/'+newProductID] = newReceipt;

    return clicks.child('VigLink').orderByChild('item').equalTo(oldProductID).once('value', function(cuidSnapshot) {
        return cuidSnapshot.forEach(function(cuidSnapshot) {
            var cuid = cuidSnapshot.key;
            updateObject['clicks/VigLink/'+cuid+'/item'] = newProductID;
            progress(10);
        });
    }).then(function() {
        return userReceiptMetrics.child(userID).child('receipts').child(receiptID).child('items').child(oldProductID).once('value', function(oldSnapshot) {
            var data = oldSnapshot.val()
            updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/items/'+oldProductID] = null
            updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/items/'+newProductID] = data
            if (data != null) {
                updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/itemIDs/'+newProductID] = now
                updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/itemIDs/'+oldProductID] = null
            };
            progress(25);
        });
    }).then(function() {
        return userReceiptMetrics.child(userID).child('shops').child(oldProductID).once('value', function(oldSnapshot) {
            var data = oldSnapshot.val()
            updateObject['userReceiptMetrics/'+userID+'/shops/'+oldProductID] = null;
            updateObject['userReceiptMetrics/'+userID+'/shops/'+newProductID] = data;
            if (data != null) {
                updateObject['userReceiptMetrics/'+userID+'/shopIDs/'+newProductID] = now;
                updateObject['userReceiptMetrics/'+userID+'/shopIDs/'+oldProductID] = null;
            };
            progress(40);
        });
    }).then(function() {
        progress(55);
        return posts.orderByChild('receipt').equalTo(receiptID).once('value');
    }).then(function(postSnapshot) {
        return postSnapshot.forEach(function(post) {
            progress(70);
            postKeys.push(post.key)
        });
    }).then(function() {
        return Promise.all(postKeys.map(function(postKey) {
            return posts.child(postKey).child('items').child(oldProductID).once('value', function(itemSnapshot) {
                var itemData = itemSnapshot.val()
                updateObject['posts/'+postKey+'/items/'+oldProductID] = null;
                updateObject['posts/'+postKey+'/items/'+newProductID] = itemData;
            });
        })).then(function(results) {
            progress(85);
            return results;
        });
    }).then(function() {
        return firebaseRoot.update(updateObject, function(error) {
            if (error) {
                console.log("Error updating data:", error);
                reject()
            } else {
                progress(100);
                resolve();
            }
        });
    });

    // Finish the task asynchronously
    setTimeout(function() {
        reject();
    }, 10000);
});