Reputation: 5080
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
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