Reputation: 12862
I have the following:
https://jsfiddle.net/qofbvuvs/
var allItems = ["1", "2", "3"]
var allPeople = ["A", "B"]
var testFoo = function(itemValue, peopleValue) {
setTimeout(function(){
return itemValue == "3" && peopleValue == "B"
}, 200)
}
allItems.forEach(function(itemValue) {
allPeople.forEach(function(peopleValue) {
// I want to iterate through each object, completing testFoo before moving on to the next object, without recursion. TestFoo has a few instances of setTimeout. I've tried using promises to no avail.
if (testFoo(itemValue, peopleValue)){
alert("success")
} else{
// nothing
};
})
})
alert("complete")
My goal is to iterate through each item, one by one, in order, while waiting for the results of testFoo
. If testFoo passes, then I should stop execution.
I've tried to use promises (https://jsfiddle.net/qofbvuvs/2/) but can't get the behavior I'm looking for. Success
should be called before Complete
. TestFoo
has a couple setTimeouts that I need to work around (it's a library I can't modify). How can this be achieved?
Upvotes: 0
Views: 744
Reputation: 1281
Since there's already a bunch of solutions out, I figured i'd add a solution using async/await
. I personally like this one because it keeps the code similar to your original and more compact, where you don't have to keep track of any intermediate states or resolve all promises. The solution is just to wrap the for loops inside an async function and change the forEach to a basic for(var x...) because await wont work correctly within a forEach statement because you are defining an inner non async function and calling await
within that is invalid. Additionally change the testFoo
function to return a promise.
Since the for loops are inside a function you an easily exit it once a match has been found to skip further checks. I also added a return statement at the end of the loops to indicate nothing has been found, which can be handy.
Finally, since async functions themselves return a promise, all you have to do is add the final alerts inside the then
of the returned promise to evaluate if a match has been found or not.
async/await example:
var allItems = ["1", "2", "3"]
var allPeople = ["A", "B"]
var testFoo = function(itemValue, peopleValue) {
return new Promise(function(resolve, reject){
setTimeout(function(){
resolve(itemValue == "3" && peopleValue == "B");
}, 200)
});
}
async function checkItemPeople(){
for(var itm=0; itm<allItems.length; itm++){
for(var ppl=0; ppl<allPeople.length; ppl++){
var result = await testFoo(allItems[itm], allPeople[ppl]);
if (result) {
alert("success");
return true;
} else {
// nothing
};
}
}
return false;
}
checkItemPeople().then(function(resp){
resp ? alert("Found Something") : alert("Theres nothing");
});
Upvotes: 1
Reputation: 11116
One way you can do it is through jQuery deferreds and manually stepping through your arrays, rather than using the built in loops, so you can control if/when you proceed. I guess it uses recursion in a way, but for nothing more than to invoke the next iteration - there is no crazy recursive return value unraveling or anything that makes recursion complex. Let me know if this works for you:
var allItems = ["1", "2", "3"]
var allPeople = ["A", "B"]
var testFoo = function(itemValue, peopleValue) {
var deferredObject = $.Deferred();
setTimeout(function() {
deferredObject.resolve(itemValue == "3" && peopleValue == "B")
}, 200)
return deferredObject;
}
var currentItemIndex = 0;
var currentPeopleIndex = 0;
var testDeferred = $.Deferred();
function testAll() {
testFoo(allItems[currentItemIndex], allPeople[currentPeopleIndex]).done(function(result) {
if (result) {
// found result - stop execution
testDeferred.resolve("success");
} else {
currentPeopleIndex++;
if (currentPeopleIndex >= allPeople.length) {
currentPeopleIndex = 0;
currentItemIndex++;
}
if (currentItemIndex >= allItems.length) {
// result not found - stop execution
testDeferred.resolve("fail");
} else {
// check next value pair
testAll();
}
}
});
return testDeferred;
}
testAll().done(function resolveCallback (message) {
alert(message);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Upvotes: 1
Reputation: 35491
If you want to construct a chain of promises in such a way where each promise isn't created until it's predecessor promise in the chain resolves, you should create an array of functions that return your promises and use reduction to create the needed promise chain.
var allItems = ["1", "2", "3"];
var allPeople = ["A", "B"];
// Async requests aren't made once we reach "2" and "B"
var resultToStopOn = function(itemValue, peopleValue) {
return itemValue === "2" && peopleValue === "B"
}
var testFoo = function(itemValue, peopleValue) {
return new Promise(function(resolve) {
console.log('Starting promise for: ', itemValue, peopleValue);
setTimeout(function() {
console.log('Resolving promise for: ', itemValue, peopleValue);
resolve(resultToStopOn(itemValue, peopleValue));
}, 200);
});
}
var functionsThatReturnPromises = [];
allItems.forEach(function(itemValue) {
allPeople.forEach(function(peopleValue) {
functionsThatReturnPromises.push(function() {
return testFoo(itemValue, peopleValue);
});
});
});
functionsThatReturnPromises
.reduce(function(chainOfPromises, fnReturningAPromise) {
return chainOfPromises.then(function(result) {
// if result is false, we continue down the chain
// otherwise we just propagate the result and don't chain anymore
return !result
? fnReturningAPromise()
: result;
});
}, Promise.resolve())
.then((result) => {
console.log('Result: ', result);
});
Upvotes: 0
Reputation: 2294
Used Promise.all
to solve your problem. Hope this helps.
var allItems = ["1", "2", "3"];
var allPeople = ["A", "B"];
var testFoo = function(itemValue, peopleValue) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(itemValue === "3" && peopleValue === "B");
}, 200);
});
}
var promises = [];
allItems.forEach(function(itemValue) {
allPeople.forEach(function(peopleValue) {
promises.push(testFoo(itemValue, peopleValue).then(function(result) {
console.log(result?'success':'fail');
return result;
}));
})
});
Promise.all(promises).then(function(results) {
console.log('Result from all promise: ' + results);
console.log("complete");
});
Upvotes: 0