Kittoes0124
Kittoes0124

Reputation: 5080

Why can't I add to my array in this particular function?

I have a function in which I loop through and perform a small set of queries that return chunks of data using Breeze.js. Every section of the code works but one single line (where I try to add the results of the query to the array). I have tried all kinds of things and just can't seem to figure out WHY it doesn't work.

function doStuff(sourceArray, outputObservable, errorObservable) {
    var arr = [];

    _.each(sourceArray, function (sourceItem) {
        return breeze.EntityQuery
            .from("SomeTable")
            .using(manager).execute()
            .then(getSucceeded)
            .fail(getFailed);

        function getSucceeded(data) {
            _.each(data.results, function (item) {
                console.log(item); //This works
                arr.push(item);  //This doesn't work
            });
        }

        function getFailed(error) {
            errorObservable("Error retrieving data: " + error.message);
        }
    });

    outputObservable(arr);
}

This will output all of the results of each query to the console but it will not at the results to the array. However, if I change the second line to var arr = [ 1, 2, 3, 4, 5 ]; then those values will reflect in outputObservable.

EDIT: Full solution is below.

function doStuff(sourceArray, outputObservable, errorObservable) {
    var promises = [];
    var arr = [];

    //Create an array of promises
    _.forEach(sourceArray, function (item) {
        promises.push(getDataFromServer(item.val1, item.val2));
    });

    //Wait for all promises to resolve, then set output
    Q.all(promises).then(function () {
        outputObservable(arr);
    });

    function getDataFromServer(filter1, filter2) {
        var p1 = breeze.Predicate.create("field1", "==", filter1);
        var p2 = breeze.Predicate.create("field2", "==", filter2);

        return breeze.EntityQuery
            .from("SomeTable")
            .where(p1.and(p2))
            .using(manager).execute()
            .then(getSucceeded)
            .fail(getFailed);

        function getSucceeded(data) {
            _.each(data.results, function (item) {
                arr.push(item);
            });
        }

        function getFailed(error) {
            errorObservable("Error retrieving processes: " + error.message);
        }
    }
}

Upvotes: 1

Views: 72

Answers (1)

Mattias Buelens
Mattias Buelens

Reputation: 20159

Ah, the classic pitfall with asynchronism. getSucceeded is called asynchronously, meaning that you pass it as a callback to EntityQuery which in turn will call it at a later time.

By the time your _.each(sourceArray, ...) returns, all queries will have started but are still running. None of those will have completed yet, and none of your callbacks will have been called yet. Thus, arr is still unchanged by the time you pass it to outputObservable. You should be able to notice this in your debugger: add a console.log before outputObservable and you'll see that it will be called before any of the console.log(item) calls.

You only want to call outputObservable when all asynchronous queries have completed. I'm not too familiar with Breeze or underscore.js, but you need to combine the promises returned by execute() into a new promise. This new promise gets resolved when all promises become resolved, so you should attach a then callback to it and call outputObservable.

Upvotes: 4

Related Questions