Xilef
Xilef

Reputation: 29

Q - Javascript promises wait for array to be filled

I'm new to the Q library and I have understanding problems with the Q's promises for array methods. I have a array with various key strings to be replaced by a delayed function. After the passed in array got successfully replaced, I need to go on with the now fulfilled array. How do I wait to for the array to be replaced in this case?

Output looks like

 key: "p", 
 text: "Good morning %text%", 
 inside:  key: "p"
          text: "Good afternoon user"
          inside: key: "p"
                  text: "This user has to be replaced."

As you can see not all keys got replaced.

Code sample

var object = [];
    object.key = "p";
    object.text = "Good morning %text%";
    object.inside = [];
    object.inside.key = "p";
    object.inside.text = "Good afternoon %text%";
    object.inside.inside = [];
    object.inside.inside.key = "p";
    object.inside.inside.text = "This %text% has to be replaced.";

goDeep(object, 0);

console.log(object);

function goDeep(data) {
    if (data instanceof Object) {
        Object.keys(data).forEach(function(key) {
            if (data[key] instanceof Object) {
                goDeep(data[key]);
            } else if (data[key].inside) {
                goDeep(data[key].inside);
            } else {
                var match = scanText(data[key]);
                if (match && !(match instanceof Boolean)) {
                    getServerData(match.input).then (function(response) {
                        var splitData = data[key].match(/%.*%/)[0].split(",");
                        for (ii = 0; ii < splitData.length; ++ii) {
                            splitData[ii] = splitData[ii].trim();
                            var replaceData = data[key].replace(splitData[ii], response);
                            // Data gets replaced here
                            data[key] = replaceData;
                        };
                    });
                }
            }
        });
    }
}

function scanText(data) {
    var match = data.match("(%.*%)", "/g");
    if (match) {
        return match;
    } else {
        return false;
    }
}

function getServerData(data) {
    return Q.delay(1000).thenResolve("user");
}

Upvotes: 0

Views: 1321

Answers (1)

amd
amd

Reputation: 21462

First let me correct slightly what do you really want,

Calling an asynchronous function recursively using JavaScript promise

Note: I am note aware of the Q Library, but I am sure it will be similar to others promise implementation

I will try my best to simplify your problem so I will explain it in steps

1. How to turn an array of promises to a single promise

you need the all method

Q.all([
 getWeather({name:'beirut'}),
 getWeather({name:'paris'}),
 getWeather({name:'madrid'})
]).then(function(beirut, paris, madrid){
 // here I am sure that the 3 methods where completed
});

2. How to convert an array of parameters to an array of promises

using array.map

['beirut', 'paris', 'madrid'].map(function(city){ return getWeather(city) });

3. Implement a recursive mechanism

function getWeather(area) {
    // this function should return a promise of the weather response
    var deferred = Q.defer();
    setTimeout(function () {
        deferred.resolve({
            name: 'beirut',
            children: [{
                name: 'sub area'
            }, ...]
        });
    }, 300);

    return deffered.promise;
}

function resolveChildren(parentArea, level) {
    var deferred = Q.defer();

    getWeather(parentArea).then(function (children) {
        var promise = Q.all(children.map(function ( /* a sub area */ child) {
            // if this a leaf then resolve the child, else go deeper and get the children of this child
            // suppose child.isleaf return true if this is a leaf node
            if (child.isleaf) {
                return child;
            }

            return resolveChildren(child, level + 1);
        }));

        promise.then(function (children) {
            parentArea.children = children;
            deferred.resolve(parentArea);
        });
    }, function (err) {
        // failed to get children for some reason
        parentArea.children = null;
        deferred.resolve(parentArea);
    });

    return deffered.promise;
}

Upvotes: 2

Related Questions