0

I want to execute an HTTP GET request that fetches some data, then create a few "subrequests" to execute based on that data, and then repeat this cycle: big request, then some small requests based on the data returned from the big one. However, I want the next iteration of the cycle to start only after all of the "subrequests" are done. My code so far looks like:

var end = w/e; // the amount of calls I want to make
(function recursiveCall(index) {
    $http.get('blahblahblah').then(function (response) { // "big" request
        var requests = [];
        for(var i = 0; i < whatever; i++) {
            requests[i] = (function(reqIndex) { // fill the array with a series of (different) requests
                return function() {
                    setTimeout(function() {
                        // use reqIndex to create a different request
                        $http.get('blahblahblah' + reqIndex /* some modification based on i */).then(function (data) {
                            // callback
                        }, function (data) {
                            // error
                        });
                    }, 1000 * reqIndex); // need to pause between each req
                }
            })(i)
        }
        Promise.all(requests.map(function (req) { // execute the array of requests
            return req();
        })).then(function (data) { // I want this to happen only after *all* the requests are done
            // success!
            console.log('all sub-requests done!');
            if(index === end) {
                return 0;
            } else {
                recursiveCall(++index); // repeat with index + 1
            }
        }, function (data) {
            // error :(
            console.log("error");
        });
    }, function (response) {
        // error
    });
})(0);

However, the then() clause of Promise.all() seems to execute immediately after the "big" request returns with a 200; it doesn't wait for all the others to be executed. Why is this?

Bluefire
  • 13,519
  • 24
  • 74
  • 118
  • You're not filling the `requests` array with promises. You need to wrap the setTimeout with a promise, and resolve or reject it in the http callback method. – mguimard Aug 25 '15 at 11:49
  • I did think about that; the fact that `setTimeout()` doesn't return a promise, but surely the thing inside (`$http.get()`) does? Does the `setTimeout()` wrapper stop `Promise.all()` from detecting it? – Bluefire Aug 25 '15 at 11:51
  • 1
    Yes. Create a promise before the setTimeout. Resolve or reject it in the http callback. Don't forget to return the promise immediatly (after setTimeout instruction) – mguimard Aug 25 '15 at 11:53
  • How would I do that? Could you post a sample snippet as an answer? – Bluefire Aug 25 '15 at 11:54
  • 1
    Just use `return $timeout(…).then(function(){ return $http(…); })` – Bergi Aug 25 '15 at 12:16

1 Answers1

1

Here a sample code to illustrate my comment :

return new Promise(function(resolve, reject) { 
    setTimeout(function(){

      // Then, in the http callback 
      resolve(); // or reject depending on your stuff...

    }, 1000); 
});

If you're using $q (in a AngularJS app), please refer to https://docs.angularjs.org/api/ng/service/%24q

mguimard
  • 1,881
  • 13
  • 14
  • Thank you! This helps a lot. – Bluefire Aug 25 '15 at 12:04
  • Quick thing: it doesn't seem like there's any difference between the native `Promise` and Angular's `$q`. Am I missing something? – Bluefire Aug 25 '15 at 12:16
  • Avoid the [promise constructor antipattern](http://stackoverflow.com/q/23803743/1048572) if you are doing more than `resolve()` inside the timeout. Also Angular does already feature a promisified version of `setTimeout`, so you should just use that. – Bergi Aug 25 '15 at 12:17
  • $q is an AngularJS service wrapping promises for you, and falling back on a JavaScript implementation if your browser doesn't support native promises. IMO you should use $q for consistency and simplicity. FYI $q is based on https://github.com/kriskowal/q – mguimard Aug 25 '15 at 12:18