0

The closest answer I could find was this https://stackoverflow.com/a/17216555/2834734

The most common use for .then is chaining ajax requests:

$.ajax({...}).then(function(){ return $.ajax({...}); }).then(function(){ return $.ajax({...}); }).then(function(){ return $.ajax({...}); }).then(function(){ return $.ajax({...}); });

this can easily be done in a loop

However it's the looping procedure I'm having difficulty with plus I have some unusual circumstances.

A brief explanation is, I have an array of requests that I need to loop through, some will invoke an ajax load and others will not. I need them to run consecutively but also run a specific function call at then end.

Here is a simple(I hope) sample of my situation:

      // Here is my flow of logic
       var thingsToDo = new tasks(); // Initiate the constructor, explained below

        // Loop through the requests array and process them consecutively
       for (var i in thingsToDo.requests) {
         $.when(thingsToDo.jqxhr).then(function() {
           thingsToDo.requests[i].fn();
         })
       }
        // Run my final function at the end of the chain.
       $.when(thingsToDo.jqxhr).then(function() {
         runOnceAllComplete();
       });

This is the constructor class the above is based on.

        // Constructor
       function tasks() {
         _tasks_ = this; // automatic global var 
         this.jqxhr = undefined; // Var to monitor ajax calls
         this.requests = [ // list of tasks to run.
           {
             url: 'file1.php',
             fn: function() {
               _tasks_.load(this.url);
               console.log('file1 loaded');
             }
           }, {
             url: 'file2.php',
             fn: function() {
               _tasks_.load(this.url);
               console.log('file2 loaded');
             }
           }, {
             noUrl: true, // Note there is no file to load here
             fn: function() {
               console.log('no file here to load, but process something else');
               $('body').css("background-color", "blue");
             }
           }, {
             url: 'file3.php',
             fn: function() {
               _tasks_.load(this.url);
               console.log('file1 loaded');
             }
           },
         ];
         this.load = function(file) { // This runs the ajax call and resets this.jqxhr             
           this.jqxhr = $.get(file);
         }
       }

       function runOnceAllComplete() {
         alert('hooray!, we finished');
       }

The tricky part I have is the requests are created dynamically so there can be 1-n many requests to perform, which is why I chose to loop, and they must be performed in that order.

As mentioned some requests will invoke an ajax call and others may not, this doesn't seem to break $.when().then(), but the problem is the loop continues before the promise is resolved and my final function happens before the final request. Still trying to get my head around promises, the first time I've used them.

Community
  • 1
  • 1
Nado11
  • 105
  • 1
  • 6
  • If you need to use Promises more than once, then you might want to look at a library like [bluebird](http://bluebirdjs.com/docs/getting-started.html), the Promise implementation of jQuery (before 3.x) is not the best one. And libraries like _bluebird_ include constructs like [`Promise.all`](http://bluebirdjs.com/docs/api/promise.all.html) which will help a lot in such situations. – t.niese Dec 28 '15 at 08:24
  • @t.niese Bluebird looks pretty powerful, I'm starting to feel like I need some sort of queueing service to handle this. – Nado11 Dec 28 '15 at 10:01

1 Answers1

1

Try including return statement at fn , this.load ; adding .promise() chained to $("body") at fn to return a jQuery promise object ; using Function.prototype.apply() , $.map() at $.when()

fn: function() {
  // added `return`
  return _tasks_.load(this.url);               
}

this.load = function(file) { 
  this.jqxhr = $.get(file);
  // added `return`
  return this.jqxhr
}

fn: function() {
  console.log('no file here to load, but process something else');
  // added `return` , `.promise()`
  return $('body').css("background-color", "blue").promise();
}

$.when.apply($, $.map(thingsToDo.requests, function(task) {
  return task.fn()
})).then(runOnceAllComplete)

See also Pass in an array of Deferreds to $.when() , What does $.when.apply($, someArray) do?


however I'm encountering a problem, using the .map() it doesn't wait for each request to complete before processing the next one. I need each one to complete before moving to the next.

Try using .queue() , which will calls functions in queue sequentially, and only when next is called at current function

$(thingsToDo).queue("tasks", $.map(thingsToDo.requests, function(task) {
  return function(next) {
    // call next function in `"tasks"` queue
    // when current function completes using `.then(next)`
    return task.fn().then(next)
  }
})).dequeue("tasks").promise("tasks").then(runOnceAllComplete)

See .queue() , .promise() , Execute function queue in javascript

Community
  • 1
  • 1
guest271314
  • 1
  • 15
  • 104
  • 177
  • I think this looks correct, but you should explain the `$.when.apply($, $.map(thingsToDo.requests, function(task) {` because I would guess, that if the OP has problem with using Promises, then this construct might also not be self explaining. – t.niese Dec 28 '15 at 08:21
  • Thanks for your help, took a while to apply this to my code but I got it there. I really do like this method, however I'm encountering a problem, using the `.map()` it doesn't wait for each request to complete before processing the next one. I need each one to complete before moving to the next. – Nado11 Dec 28 '15 at 09:57
  • @Nado11 See updated post, added alternative approach using `.queue()` – guest271314 Dec 28 '15 at 15:15
  • Works really well, the only thing I slightly altered was `$({}).queue(...)` as looking into the function I didn't need to pass anything through there. – Nado11 Dec 29 '15 at 01:28
  • @Nado11 _"the only thing I slightly altered was $({}).queue(...) as looking into the function I didn't need to pass anything through there."_ Yes, could pass an object object to `.queue()` ; note passing `thingsToDo` to `jQuery()` allows accessing `thingsToDo` as `this` within function returned from `$.map()` – guest271314 Dec 29 '15 at 16:37