14

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
Fabis
  • 1,932
  • 2
  • 20
  • 37
  • Typically with that much code people don't even bother reading your question. – Sterling Archer Apr 14 '14 at 20:13
  • You don't have to read the whole thing, the basic question is the same - there's a foreach loop with $.get functions, and I need them all to complete before moving on. I'll shorten the code tho, I guess... – Fabis Apr 14 '14 at 20:17

4 Answers4

18

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
})
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • I 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? – Fabis Apr 14 '14 at 21:52
  • @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. – Benjamin Gruenbaum Apr 14 '14 at 21:54
  • If I used `return process() ` then I'll get the error: `Unhandled rejection TypeError: Property 'process' of object # is not a function` Makes me think I shouldn't be using process function when return an array of objects – Jamie Hutber Jul 19 '15 at 13:23
  • @JamieHutber that error indicates you didn't write the code you say you wrote. You wrote `something.process(...)` and that something didn't have a `process` method. – Benjamin Gruenbaum Jul 19 '15 at 13:25
10

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)
  })
}
MJV
  • 1,782
  • 2
  • 21
  • 33
  • Hi, can you give another example without using bluebird? I just need Promise.each. Using bluebird I got big compiled file. It's not good – Rohman HM Jan 12 '17 at 07:30
  • If 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. – MJV Jan 12 '17 at 12:59
1

Today if I needed to do it sequentially - I would do it with async/await:

//I'm assuming I'm inside an `async` function
zippyarray; // array of Zippy objects

for(const task of zippyArray) {
  const result = await $.get({ ... });
  // do stuff with result
}
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
0

To keep track of multiple get-Requests you are using this way:

var cnt = requestCnt;

function finished(){
    if(--cnt)return;
    // Your code here
}

for(var i = 0; i < requestCnt; ++i){
    $.get('something.htm', {data:data}, function(data){
        finished();
    });
}

You always call the finished-function when a request gets an answer. The finished-function does the job when all gets done.

Fuzzyma
  • 7,619
  • 6
  • 28
  • 60
  • 1
    This is manually implementing logic that already exists with promises. It's called an asynchronous semaphore btw. – Benjamin Gruenbaum Apr 14 '14 at 20:21
  • 1
    Nice 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 – Fuzzyma Apr 14 '14 at 20:23
  • You 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 */ });` – Benjamin Gruenbaum Apr 14 '14 at 20:25
  • 2
    You 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. – Fuzzyma Apr 14 '14 at 20:29
  • Did 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. – Benjamin Gruenbaum Apr 14 '14 at 20:30