1

I'm trying to create merge fields in MailChimp from a list if they don't already exist. If they don't exist, I want them to be pushed to a list of promises, where I use promises.all to make sure that all the necessary list items have been added.

This isn't working though. What am I doing wrong?

var productDict = []

var getMergeNumPromise = new Promise( 
    function(resolve, reject) {
        // call to mailchimp to get the number of merge fields
        rp({
            uri: MAILCHIMP_MERGEFIELDS_URI, 
            qs:{count:1}, 
            json:true, 
            headers:MAILCHIMP_HEADER
        })
        .then(function( mergeFieldList ) {
            console.log("total items: " + mergeFieldList.total_items)
            resolve(mergeFieldList.total_items)
        })
        .catch(function(err) {
            console.log("error getting merge field count: " + err)
            reject(err)
        })
    }
)

var getMergeFieldsPromise = new Promise( 
    function( resolve, reject ) {
        getMergeNumPromise.then(function( total, err ){
            //gets just the name and tag for all merge fields in the list
            rp({
                uri: MAILCHIMP_MERGEFIELDS_URI, 
                qs:{
                    count: total,
                    fields: "merge_fields.tag,merge_fields.name"
                },
                headers: MAILCHIMP_HEADER
            })
            .then(function( fullFieldList ) {
                console.log("FULL FIELD BODY" + fullFieldList)
                var body = JSON.parse(fullFieldList)
                resolve(body.merge_fields)
            })
            .catch(function(err){
                console.log("error getting fields: " + err)
                reject(err)
            })
        })
    }
)

function addMergeField (prodName , dictPos) {
    return new Promise (
        function(resolve, reject) {
            fieldBody = { name : prodName , type : "number"}

            //post request to make the new merge field
            rp({
                method: "POST", 
                uri: MAILCHIMP_MERGEFIELDS_URI, 
                json: true, 
                headers: MAILCHIMP_HEADER, 
                body: fieldBody
            })
            .then(function(body) {
                //update product dictionary
                productDict[dictPos] = {tag : body.tag, name : body.name}
                console.log("MERGE FIELD RESPONSE " + JSON.stringify(body))
                resolve(body)
            })
           .catch(function(err) {
                console.log("error creating merge field for product id: " + err)
                reject(err)
            })
        }
    )
}

var updateMergeFields = getMergeFieldsPromise.then( 
    function( mergeFieldList ) {
        // resolved ids keeps track of ids that have already been added
        var resolvedIDS = {}
        //holds result of find to look for product ids
        var foundMCMatch
        // holds productIDS[i]
        var product
        //console.log("merge field list" + JSON.stringify(mergeFieldList))

        for(var i = 0; i < productIDS.length; i++) {
            console.log("checking if product id " + productIDS[i] + "exists")
            product = productIDS[i]
            // tries to find a match to see if fields are already in mailchimp
            foundMCMATCH = mergeFieldList.find(x => x.name == product)

            if(foundMCMATCH) {
                console.log("foundMCMATCH" + JSON.stringify(foundMCMATCH))

                //updates product dict with matching tag/name from mailchimp
                productDict[i] = {
                    tag : foundMCMATCH.tag, 
                    name : foundMCMATCH.name
                }
                //console.log("PRODUCT DICT " + JSON.stringify(productDict))
            }

            //if field isn't on mailchimp
            else if (!resolvedIDS[product])
            {
                resolvedIDS[product] = true

                // adds product id as merge field becasue it doesn't exist
                allProductIDPromises.push(
                    addMergeField(product,i)
                )
            }
        }
    }
)

allProductIDPromises.push( getMergeFieldsPromise, getMergeNumPromise, updateMergeFields ) 
Promise.all(allProductIDPromises) 
.then(function() { 
    //function here that's running out of order
}

NB: I'm using request promise to make my post requests, so they are already promisified.

trincot
  • 317,000
  • 35
  • 244
  • 286
cclos
  • 13
  • 5
  • What do you do with `allProductIDPromises`? – trincot Aug 25 '17 at 07:23
  • allProductIDPromises.push( getMergeFieldsPromise, getMergeNumPromise, updateMergeFields ) Promise.all(allProductIDPromises) .then(function() { the function that's running out of order} – cclos Aug 25 '17 at 07:24
  • I have edited your question to improve the indentation of your code. I also removed phrases which only distract from the question (people here want to understand your question from the first few phrases, so skip all the introduction, and how you are stumped, etc). Could you add the code on exactly where you call `Promise.all`? Note also that you mentioned `promises.all` in your question, not `Promise.all`. – trincot Aug 25 '17 at 07:31
  • before all the merge fields are created the function after Promise.all is running (which isn't intended) and it's causing errors later on in my code when it's trying to access the updates to the productDict variable that haven't happened yet (since they are triggered right after the POST request). I guess i'm confused about how the function in Promise.all will run even though i'm adding promises that at the time should be pending – cclos Aug 25 '17 at 07:38
  • I just edited the post because I meant to express that i'm only pushing them to a list of promises if they are being created (since I need use information returned from their creation and need to be sure that my program isn't accessing merge fields that don't exist yet) – cclos Aug 25 '17 at 07:43
  • What are `getMergeFieldsPromise, getMergeNumPromise`? – trincot Aug 25 '17 at 07:53
  • just added them in an edit – cclos Aug 25 '17 at 07:58
  • Have a look at my answer. – trincot Aug 25 '17 at 08:14
  • Avoid the [`Promise` constructor antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it)! – Bergi Aug 25 '17 at 08:32

1 Answers1

0

The then callback of getMergeFieldsPromise is not returning anything, so once getMergeFieldsPromise is resolved, it does not await the addMergeField promises, but instead resolves immediately (with undefined as promised value).

To solve this, make allProductIDPromises a local variable inside the then callback and call Promise.all on it, and return it there:

var updateMergeFields = getMergeFieldsPromise.then( 
    function( mergeFieldList ) {
        // **** make the array of promises local to this function
        var allProductIDPromises = [];
        // resolved ids keeps track of ids that have already been added
        var resolvedIDS = {}
        //holds result of find to look for product ids
        var foundMCMatch
        // holds productIDS[i]
        var product
        //console.log("merge field list" + JSON.stringify(mergeFieldList))

        for(var i = 0; i < productIDS.length; i++) {
            console.log("checking if product id " + productIDS[i] + "exists")
            product = productIDS[i]
            // tries to find a match to see if fields are already in mailchimp
            foundMCMATCH = mergeFieldList.find(x => x.name == product)

            if(foundMCMATCH) {
                console.log("foundMCMATCH" + JSON.stringify(foundMCMATCH))

                //updates product dict with matching tag/name from mailchimp
                productDict[i] = {
                    tag : foundMCMATCH.tag, 
                    name : foundMCMATCH.name
                }
                //console.log("PRODUCT DICT " + JSON.stringify(productDict))
            }

            //if field isn't on mailchimp
            else if (!resolvedIDS[product])
            {
                resolvedIDS[product] = true

                // adds product id as merge field becasue it doesn't exist
                allProductIDPromises.push(
                    addMergeField(product,i)
                )
            }
        }
        // **** call Promise.all here, and return it
        return Promise.all(allProductIDPromises);
    }
)

Then after that, you should remove this:

allProductIDPromises.push( ... )

... and since your main three promises are already chained with then, you only need to perform a then on the last one:

updateMergeFields.then(function(addedMergeFields) { 
    // ... all is ready now.
}

Finally, read about the Promise constructor anti-pattern, which you are using several times: you should not use new Promise when in fact you can return a promise (e.g. return rp()).

trincot
  • 317,000
  • 35
  • 244
  • 286
  • thank you so much! it makes so much sense now that you explained it! i would upvote you but i dont have the reputation :( – cclos Aug 25 '17 at 08:50