How do I make this async foreach loop work with promises?
Solution 1
In synchronous code, continuation is performed when the line ends ;
With promises, continuation is performed via .then
. You were using a promise constructor and resolved it immediately, you did not wait for any task at all. I'd map my work into tasks and then either chain them with then or await them serially.
//I'm assuming
zippyarray; // array of Zippy objects
var tasks = zippyarray.map(function(zippy,i){
return function(){ // return a task on that zippy;
// basic logic here
return $.get({
// ajax request
}).then(function(data){
// process data like in your code
// possibly store later for later use too
return process(data); // return the processed data;
});
}
});
Now we can execute them all sequentially:
var p = tasks[0](); // start the first one
for(var i = 1; i < tasks.length; i++) p = p.then(tasks[i]);
p.then(function(result){
// all available here
});
Or better, serially:
$.when.apply(tasks.forEach(function(t){ return t(); })).then(function(results){
// all done
})
Solution 2
I know this is an old question but things have changed a bit recently.
If you're fine with using external libraries, the Bluebird promise library has a pretty good implementation for this: Promise.each.
E.g.
function helperFunc(zippyarray) {
return Promise.each(zippyarray, zippy => {
return someOperationThatReturnAPromise(zippy)
.then((singleResult) => {
// do something with the operation result if needed
})
}).then((originalArray) => {
// this happens only after the whole array is processed
// (result is the original array here)
return Promise.resolve(originalArray)
})
}
Fabis
Updated on November 12, 2020Comments
-
Fabis over 3 years
I've already messed around with Promises in it, but I'm new to them and I just can't figure out how to do it properly. At the moment, there's no point to the Promise, because it doesn't wait till the async
$.get
completes.Basically, each
foreach
iteration has its own$.get
function, and I need to have them all complete and then continue to the part that has the "...gets albumart"console.log
.$.get(id,function(data) { //(there's some code here) var getZippyUrls = new Promise(function(resolve) { zippyarray.forEach(function(zippy) { //(more code) $.get(zippy.full, function(data) { //^This is the foreach of $.gets //(code's here) }); resolve(zippyarray); }); }); //This is my failed Promise -> getZippyUrls.then(function(response) { console.log("WE'RE OUT " + response.length); response.foreach(function(d) { console.log("Promise"+d.media); }); console.log('eyyyyyy'); }); console.log("...gets albumart"); //Now after the previous stuff is done, move on
-
Benjamin Gruenbaum about 10 yearsThis is manually implementing logic that already exists with promises. It's called an asynchronous semaphore btw.
-
Fuzzyma about 10 yearsNice to know the name. Its never wrong to know the code behind the function. Especially if you are laging the jquery-lib on your page
-
Benjamin Gruenbaum about 10 yearsYou don't need the jQuery library, you can use either a modern browser (promises are a part of the new ES6 specification), or a good promise library (like Bluebird). Having to manually do this like your code disregards a lot of things (error handling, etc). It could be written as
$.when.apply(requests.map(function(el){ return $.get("something.html",{data:el}); })).then(function(results){ /* results all available here */ });
-
Fuzzyma about 10 yearsYou already wrote that in your answer. Note that my answer still isnt wrong. Maybe there are mroe modern ways but this way will work definitevely no matter which browser or lib you are using.
-
Benjamin Gruenbaum about 10 yearsDid I claim it was wrong anywhere? All I was saying is that I think it's not the best solution given the circumstance (quite a poor one imo) and I explained why (no error handling, manual wiring, repeating code, etc). I didn't mean to imply otherwise.
-
Fabis about 10 yearsI don't understand why are there 3 returns within the map() function. Could you clear that up for me? What do each of them do?
-
Benjamin Gruenbaum about 10 years@Fabis the outer return returns the task. We map each zippy to a function. The second return is from the task function and returns the result of
$.get
(a promise). The third return is for returning a value from a.then
handler. In general, when you return a value from a then handler it will resolve the promise with that value, if that value is a promise itself it will resolve the outer promise with its unwrapped value. -
Jamie Hutber almost 9 yearsIf I used
return process()
then I'll get the error:Unhandled rejection TypeError: Property 'process' of object #<Object> is not a function
Makes me think I shouldn't be using process function when return an array of objects -
Benjamin Gruenbaum almost 9 years@JamieHutber that error indicates you didn't write the code you say you wrote. You wrote
something.process(...)
and that something didn't have aprocess
method. -
Rohman HM over 7 yearsHi, can you give another example without using bluebird? I just need Promise.each. Using bluebird I got big compiled file. It's not good
-
MJV over 7 yearsIf you don't want to use libraries (possibly apart from jQuery which is used in the question itself), I think Benjamin's answer could be the way to go. Or if you're using ES6, you could add all the desired function calls in a collection and then use
Promise.all
with that.