49

I have that code :

for (var i = 0; i < $total_files; i++) {
  $.ajax({
    type: 'POST',
    url: 'uploading.php',
    context: $(this),
    dataType: 'json',
    cache: false,
    contentType: false,
    processData: false,
    data: data_string,
    success: function(datas) {
      //does something
    },
    error: function(e) {
      alert('error, try again');
    }
  });
}

It uploads images very well but the problem is that I can't find a way to upload the images one by one, I tried to put the option async to false but it freezes the web browser until all images are uploaded which is not what I want, I want to emulate somehow this "async : false" option to perform the same thing but without freezing the web browser.

How to do this ?

Hakan Fıstık
  • 16,800
  • 14
  • 110
  • 131
tcj
  • 1,645
  • 4
  • 13
  • 21
  • what is overall objective? – charlietfl Nov 29 '13 at 18:21
  • It's to show each image uploaded as soon as it is uploaded to the server, each side by side, I'm trying all the solutions posted and will see which one suits my needs. – tcj Nov 29 '13 at 19:13
  • promises are the way to go, solutions suggesting upload one at a time will take far longer than necessary – charlietfl Nov 29 '13 at 19:17
  • Ok guys, I tested all your solutions but none did work the way I want :( – tcj Nov 30 '13 at 15:35
  • you never outlined behavior you want. You only stated you wanted similar to asynch false, but not why – charlietfl Nov 30 '13 at 15:41
  • I'll explain it clearly : I'm uploading a bunch of photos, let's say 20, what I want is the first photo to be displayed first on the web page, then the second, then the third and so on, with async ajax calls I get (all the solutions provided here included) like the first photo is displayed first but then it goes randomly like the third is displayed in the place of the second, etc... – tcj Nov 30 '13 at 16:01
  • ok..so instead of appending in success handler, push image to array with index of the call being made, then when promise resolves return that array and insert into dom by looping over array...beware proper way to track index in `for` loop. WOuld be simpler if use `$.each` instead for closure and use it;s indexing to manag array – charlietfl Nov 30 '13 at 16:05
  • also...if you used nested ajax calls, as some solutions suggest..what you are saying doesn't make sense since they don't occur concurrently – charlietfl Nov 30 '13 at 16:12
  • How do I get the index of the call being made ? – tcj Nov 30 '13 at 22:31
  • actuall come to think of it....you already have file data in some array that's in order you want since you have to send that data for upload...unless you change file names on server – charlietfl Nov 30 '13 at 23:16
  • File name are changed on server side, yes. – tcj Dec 01 '13 at 12:37
  • Ok, I think I get what you're talking about, gonna try it asap, thanks. – tcj Dec 01 '13 at 12:38

5 Answers5

81

You can create an array of promises so that once all promises are resolved you can run your all done code.

var promises = [];
for (var i = 0; i < $total_files; i++){ 
   /* $.ajax returns a promise*/      
   var request = $.ajax({
        /* your ajax config*/
   })

   promises.push( request);
}

$.when.apply(null, promises).done(function(){
   alert('All done')
})

DEMO

Hakan Fıstık
  • 16,800
  • 14
  • 110
  • 131
charlietfl
  • 170,828
  • 13
  • 121
  • 150
  • 1
    is your `done` syntax correct? ...OK, I didn't see your edit but now – JFK Nov 29 '13 at 18:53
  • I looked at your demo, and although it calls 'All done' at the end, the order of the execution is mixed up. Is there any way to correct this? – iptq Aug 05 '14 at 19:23
  • @failed.down best to ask a new question if you don't find answer – charlietfl Aug 06 '14 at 06:55
  • 2
    it's ok i found it, i just used ajax instead of get and replaced jsonp with json and used `async:false` in the ajax options – iptq Aug 06 '14 at 23:47
  • 4
    This is helpful but the .done function won't execute if any of the ajax calls fail – Gordon Thompson Feb 09 '16 at 12:37
  • I really liked the approach, but when i try to get response, i get response only from the first response. Is there any way to get all the responses? – v.coder Jan 05 '17 at 20:19
  • @charlietfl Where does one use `arguments`? Could you provide a code example? – Nse Feb 27 '18 at 15:35
  • this doesn't seem to execute ajax 1 by 1. – neobie Dec 07 '18 at 12:33
  • I used the above code to make a 1000 Ajax calls, but what I see in the waterflow diagram is that the requests are going serially, and very slow for me. The requests are in (pending) stalled state for a long time. Is there a way I can fire all the 1000 Ajax calls all at once? – insanely_sin Oct 14 '20 at 22:26
4

For jQuery 3.x+ and modern browser that support native Promise, Promise.all could be used this way:

var promises = [];
for (var i = 0; i < $total_files; i++) {
   // jQuery returns a prom 
   promises.push($.ajax({
      /* your ajax config*/
   }))
}

Promise.all(promises)
.then(responseList => {
   console.dir(responseList)
})

If your files are already stored in a list then you could use map instead of a loop.

var fileList = [/*... list of files ...*/];

Promise.all(fileList.map(file => $.ajax({
      /* your ajax config*/
})))
.then(responseList => {
   console.dir(responseList)
})
t.niese
  • 39,256
  • 9
  • 74
  • 101
3

Populate an array with each call and call the next item when the previous is done.

You could try something like that:

    window.syncUpload = {

        queue : [],

        upload : function(imagesCount) {

            var $total_files = imagesCount, data_string = "";

            /* Populates queue array with all ajax calls you are going to need */
            for (var i=0; i < $total_files; i++) {       
                this.queue.push({
                    type: 'POST',
                    url: 'uploading.php',
                    context: $(this),
                    dataType: 'json',
                    cache: false,
                    contentType: false,
                    processData: false,
                    data: data_string,
                    success: function(datas) {
                    //does something
                    },
                    error: function(e){
                        alert('error, try again');
                    },
                    /* When the ajax finished it'll fire the complete event, so we
                       call the next image to be uploaded.
                    */
                    complete : function() {
                        this[0].uploadNext();
                    }
                });
            }

            this.uploadNext();
        },

        uploadNext : function() {
            var queue = this.queue;

            /* If there's something left in the array, send it */
            if (queue.length > 0) {
                /* Create ajax call and remove item from array */
                $.ajax(queue.shift(0));
            }


        }

    }

Just call it using syncUpload.upload(NUMBER_OF_IMAGES);

fedevegili
  • 341
  • 3
  • 5
2

I would try jQuery.when so you can still use asynchronous call but deferred, something like :

jQuery(document).ready(function ($) {
    $.when(
        //for (var i = 0; i < $total_files; i++) {
            $.ajax({
                // ajax code
            })
        //}
    ).done(function () {
        // perform after ajax loop is done
    }); 
}); // ready

EDIT : ajax iteration should be done outside $.when and pushed into an array as proposed by charlietfl's answer. You may use an (asynchronous) ajax call and defer it inside $.when though, see JSFIDDLE

Community
  • 1
  • 1
JFK
  • 40,963
  • 31
  • 133
  • 306
  • 1
    You forgot to place a function around the `for` loop. And isn't `$.when` expecting a promise or an array of promises? – t.niese Nov 29 '13 at 18:55
  • I get "SyntaxError: missing : after property id for (var i=0; i<$total_files; i++) {" – tcj Nov 29 '13 at 19:01
  • 1
    I just wrote it quickly but I guess you should go for [charlietfl](http://stackoverflow.com/users/1175966/charlietfl)'s answer (I will delete this one) – JFK Nov 29 '13 at 19:05
  • @t.niese : btw, you can use an ajax call inside `$.when` without an `array` ... here it's charlietfl's tweaked [jsfiddle](http://jsfiddle.net/y7yhd/) ... of course, works fine for a single ajax call – JFK Nov 29 '13 at 19:07
  • Actually, I won't delete my answer since the discussion can be useful for future visitors ;) – JFK Nov 29 '13 at 19:11
0

In one statement with jquery

$.when.apply(null, $.map(/*input Array|jQuery*/, function (n, i) {
   return $.get(/* URL */, function (data) {
     /* Do something */
   });
})).done(function () {
  /* Called after all ajax is done  */
});
Godsayah
  • 134
  • 2
  • 10