web-dev-qa-db-fra.com

Comment attendre que la requête jQuery ajax se termine en boucle?

J'ai ce 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');
    }
  });
}

Il télécharge très bien les images mais le problème est que je ne peux pas trouver un moyen de télécharger les images une par une, j'ai essayé de mettre l'option async à false mais cela fige le navigateur Web jusqu'à ce que toutes les images sont téléchargés ce qui n'est pas ce que je veux, je veux émuler en quelque sorte cette option "async: false" pour effectuer la même chose mais sans geler le navigateur Web.

Comment faire ça?

38
tcj

Vous pouvez créer un tableau de promesses afin qu'une fois toutes les promesses résolues, vous puissiez exécuter votre 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

64
charlietfl

Pour jQuery 3.x + et un navigateur moderne qui prend en charge le Promise natif, Promise.all Pourrait être utilisé de cette façon:

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)
})

Si vos fichiers sont déjà stockés dans une liste, vous pouvez utiliser map au lieu d'une boucle.

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

Promise.all(fileList.map(file => $.ajax({
      /* your ajax config*/
})))
.then(responseList => {
   console.dir(responseList)
})
3
t.niese

Remplissez un tableau à chaque appel et appelez l'élément suivant lorsque le précédent est terminé.

Vous pouvez essayer quelque chose comme ça:

    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));
            }


        }

    }

Appelez-le simplement à l'aide de syncUpload.upload (NUMBER_OF_IMAGES);

3
fedevegili

J'essaierais jQuery.when pour que vous puissiez toujours utiliser l'appel asynchrone mais différé, quelque chose comme:

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

[~ # ~] modifier [~ # ~] : l'itération ajax doit être effectuée en dehors de $.when et poussé dans un tableau comme proposé par la réponse de charlietfl . Vous pouvez utiliser un appel ajax (asynchrone) et le reporter dans $.when cependant, voir JSFIDDLE

2
JFK

Dans une déclaration avec 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  */
});
0
Godsayah