-1

I have the following recursive function that works beautifully when the output from transformItem is synchronous, but I've been having a very difficult time figuring out how to refactor it when transformItem returns a a promise and still get the same type of desired final object output.

function transformStack(target, stack){
        var stackItem = stack.shift();//Copy Value

        util.logData(_this.context, "Target:" + JSON.stringify(target) + " Stack:" + JSON.stringify(stack), 9);
        switch(stackItem){
            case "[]":
                for(var x=0; x < target.length; x++){
                    //Copies values so not by Ref any more
                    var nextTarget = JSON.parse(JSON.stringify(target[x]));

                    if(stack.length > 0){
                        util.logData(_this.context, "Loop[]:" + JSON.stringify(nextTarget), 8);
                        target[x] = transformStack(nextTarget, JSON.parse(JSON.stringify(stack)));
                    } else {
                        util.logData(_this.context, "TransformTarget[]:" + JSON.stringify(nextTarget), 8);
                        target[x] = transformItem(nextTarget);
                    }
                }
                break;
            default:
                //Copies values so not by Ref any more
                var nextTarget = JSON.parse(JSON.stringify(target[stackItem]));

                if(stack.length > 0){
                    util.logData(_this.context, "Loop:" + JSON.stringify(nextTarget), 8);
                    target[stackItem] = transformStack(nextTarget, JSON.parse(JSON.stringify(stack)));
                } else {
                    util.logData(_this.context, "TransformTarget:" + JSON.stringify(nextTarget), 8);
                    target[stackItem] = transformItem(nextTarget);
                }
        }
        return target;
    }

I created this base JSFiddle which illustrates this a little better on what I'm expecting:

https://jsfiddle.net/fxay76k8/9/

Can anyone help point me in the right direction? I've been looking at the following Stack Overflow post, but haven't been able to apply it properly to my flow: JavaScript : Calling Recursive Functions With Promises

Thank-you for your help!

BTW, I'm using Q for the promises, but I'm pretty confident I can translate any other promise libraries over to what I need if someone can help me with the concepts here.

Doug
  • 6,446
  • 9
  • 74
  • 107
  • Just use `async`/`await`? – Bergi May 01 '18 at 16:22
  • are you able to use javascript 1.7? – Thatalent May 01 '18 at 16:48
  • @Bergi I am not sure what you're talking about with async/await, I did just get it working about 15 minutes ago and I posted my answer below. – Doug May 01 '18 at 16:49
  • @Thatalent Unfortunately, we can't use 1.7. – Doug May 01 '18 at 16:50
  • @Doug You're not serious? [JS 1.7 was released in 2006](https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript/1.7). – Bergi May 01 '18 at 16:53
  • @Doug I mean that with `async`/`await`, it should be a breeze to rewrite code to call promise-returning functions. – Bergi May 01 '18 at 16:57
  • 1
    @Bergi You are correct. I was thinking ECMAScript 7, not JS 1.7. JS 1.7 is fine. I hadn't heard of async/await and originally though you were referencing a library, found this here https://javascript.info/async-await , that definitely could have worked. Thanks for the suggestion. – Doug May 01 '18 at 17:11

1 Answers1

0

I was able to get this working with the following refactored code:

function transformStack(target, stack){
    var stackItem = stack.shift();//Copy Value
    var targetCopy = JSON.parse(JSON.stringify(target));
    var stackCopy = JSON.parse(JSON.stringify(stack));

    if(stackItem === "[]"){
        return transformStackArray(targetCopy, stackCopy);
    }

    var nextTarget = targetCopy[stackItem];
    if (stack.length > 0)
        return transformStack(nextTarget, stackCopy).then(handleResponse.bind(this,target, stackItem));
    else
        return transformItem(nextTarget).then(handleResponse.bind(this,target, stackItem));
}

/**
 * handleResponse - Helper function for merging objects for recursive function.
 * @param original - The original passed value
 * @param attribute - The name of the field to send
 * @param updated - The updated value
 * @returns {*} - Promise - Resolves to the merged object
 */
function handleResponse(original, attribute, updated){
    original[attribute] = updated;

    return q(original);
}

function transformStackArray(target, stack){
    var deferred = q.defer();
    var promises = [];
    for(var x=0; x < target.length; x++){
        var nextTarget = JSON.parse(JSON.stringify(target[x])); //Copies values so not by Ref any more
        if(stack.length > 0){
            promises.push(transformStack(nextTarget, JSON.parse(JSON.stringify(stack))));
        } else {
           promises.push(transformItem(nextTarget));
        }
    }

    q.all(promises).then(function(updatedItem){
        deferred.resolve(updatedItem);
    }).catch(function(error){
        deferred.reject(error);
    });

    return deferred.promise;
}

The big piece for me was wrapping my head around how to merge the objects as the new values go up the recursion. This is handles by handleResponse.

Now that I have this answer, how the linked StackOverflow relates makes a ton of sense but for some reason I had a really difficult time wrapping my head around this problem at first.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Doug
  • 6,446
  • 9
  • 74
  • 107
  • You missed to post `transformStackArray`, which is the interesting part? – Bergi May 01 '18 at 16:55
  • @Bergi I went ahead and added that function. I thought that part was pretty straight forward since it was just iterating through an array and creating an array of promises, but it definitely makes the answer better to include it. – Doug May 01 '18 at 17:00
  • Well I did consider the whole thing pretty straightforward, but collecting the promises in an array and using `Q.all` seemed to be the hardest part :-) – Bergi May 01 '18 at 18:06
  • 1
    Btw, you should avoid the [deferred antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it)! Just `return Q.all(promises);`. – Bergi May 01 '18 at 18:07
  • @Bergi I guess what is the most complicated is all dependent on your experience :-). Thank-you for your help, I'll see if I can clean-up my code more and get rid of the anti-patterns! – Doug May 01 '18 at 18:09