web-dev-qa-db-fra.com

Attendez que toutes les demandes jQuery Ajax soient terminées?

Comment faire en sorte qu'une fonction attende que toutes les demandes Ajax de jQuery soient effectuées dans une autre fonction?

En bref, je dois attendre que toutes les demandes Ajax soient effectuées avant d'exécuter la prochaine. Mais comment?

630
jamietelin

jQuery définit maintenant un when function à cette fin.

Il accepte un nombre quelconque d'objets différés en tant qu'arguments et exécute une fonction lorsque tous les résolvent.

Cela signifie que si vous voulez lancer (par exemple) quatre demandes ajax, puis exécuter une action quand elles sont terminées, vous pouvez faire quelque chose comme ceci:

$.when(ajax1(), ajax2(), ajax3(), ajax4()).done(function(a1, a2, a3, a4){
    // the code here will be executed when all four ajax requests resolve.
    // a1, a2, a3 and a4 are lists of length 3 containing the response text,
    // status, and jqXHR object for each of the four ajax calls respectively.
});

function ajax1() {
    // NOTE:  This function must return the value 
    //        from calling the $.ajax() method.
    return $.ajax({
        url: "someUrl",
        dataType: "json",
        data:  yourJsonData,            
        ...
    });
}

À mon avis, cela donne une syntaxe propre et claire, et évite d’impliquer des variables globales telles que ajaxStart et ajaxStop, qui pourraient avoir des effets secondaires indésirables au fur et à mesure du développement de votre page.

Si vous ne savez pas à l'avance combien d'arguments ajax vous devez attendre (c'est-à-dire que vous voulez utiliser un nombre variable d'arguments), cela peut toujours être fait, mais c'est un peu plus compliqué. Voir Passer un tableau de Deferreds à $ .when () (et peut-être jQuery .when dépannage avec un nombre variable d'arguments ).

Si vous avez besoin d'un contrôle plus approfondi sur les modes de défaillance des scripts ajax, etc., vous pouvez enregistrer l'objet renvoyé par .when(). Il s'agit d'un objet jQuery Promise qui englobe toutes les requêtes ajax d'origine. Vous pouvez appeler .then() ou .fail() pour ajouter des gestionnaires de réussite/échec détaillés.

857
Alex

Si vous voulez attendre que toutes les requêtes ajax soient terminées dans votre document, quel que soit le nombre d'entre elles, utilisez simplement $.ajaxStop événement de cette façon:

_  $(document).ajaxStop(function () {
      // 0 === $.active
  });
_

Dans ce cas, il n’est pas nécessaire de deviner le nombre de demandes pouvant se terminer dans une application. Dans certains cas, les requêtes ajax peuvent faire partie de la logique interne d'une fonction, ce qui peut être assez compliqué (par exemple, appeler d'autres fonctions). Dans ce cas, vous ne pouvez pas attendre que cette fonction soit terminée avec toute sa logique plutôt que seulement attend la fin de la partie ajax.

$.ajaxStop ici peut également être lié à tout nœud HTML que vous pensez susceptible d'être modifié par ajax.

Encore une fois, le but de ce gestionnaire est de savoir quand il n'y a pas actif ajax de ne pas effacer ou réinitialiser quelque chose.

P.S. Si la syntaxe ES6 ne vous dérange pas, vous pouvez utiliser _Promise.all_ pour les méthodes connues ajax. Exemple:

_Promise.all([ajax1(), ajax2()]).then(() => {
 // all requests finished successfully
}).catch(() => {
 // all requests finished but one or more failed
})
_

Un point intéressant ici est que cela fonctionne à la fois avec les requêtes Promises et _$.ajax_. Voici jsFiddle démontrant le dernier.

280
Arsen Khachaturyan

J'ai trouvé un bonne réponse par gnarf mon moi qui est exactement ce que je cherchais :)

jQuery ajaxQueue

//This handles the queues    
(function($) {

  var ajaxQueue = $({});

  $.ajaxQueue = function(ajaxOpts) {

    var oldComplete = ajaxOpts.complete;

    ajaxQueue.queue(function(next) {

      ajaxOpts.complete = function() {
        if (oldComplete) oldComplete.apply(this, arguments);

        next();
      };

      $.ajax(ajaxOpts);
    });
  };

})(jQuery);

Ensuite, vous pouvez ajouter une requête ajax à la file d'attente comme ceci:

$.ajaxQueue({
        url: 'page.php',
        data: {id: 1},
        type: 'POST',
        success: function(data) {
            $('#status').html(data);
        }
    });
31
jamietelin

NOTE: Les réponses ci-dessus utilisent une fonctionnalité qui n'existait pas au moment où cette réponse a été écrite. Je recommande d'utiliser jQuery.when() au lieu de ces approches, mais je laisse la réponse à des fins historiques.

-

Vous pourriez probablement vous en tirer avec un simple sémaphore de comptage, bien que sa mise en œuvre dépende de votre code. Un exemple simple serait quelque chose comme ...

var semaphore  = 0,     // counting semaphore for ajax requests
    all_queued = false; // bool indicator to account for instances where the first request might finish before the second even starts

semaphore++;
$.get('ajax/test1.html', function(data) {
    semaphore--;
    if (all_queued && semaphore === 0) {
        // process your custom stuff here
    }
});

semaphore++;
$.get('ajax/test2.html', function(data) {
    semaphore--;
    if (all_queued && semaphore === 0) {
        // process your custom stuff here
    }
});

semaphore++;
$.get('ajax/test3.html', function(data) {
    semaphore--;
    if (all_queued && semaphore === 0) {
        // process your custom stuff here
    }
});

semaphore++;
$.get('ajax/test4.html', function(data) {
    semaphore--;
    if (all_queued && semaphore === 0) {
        // process your custom stuff here
    }
});

// now that all ajax requests are queued up, switch the bool to indicate it
all_queued = true;

Si vous souhaitez que cela fonctionne comme {async: false} mais que vous ne voulez pas verrouiller le navigateur, vous pouvez effectuer la même chose avec une file d'attente jQuery.

var $queue = $("<div/>");
$queue.queue(function(){
    $.get('ajax/test1.html', function(data) {
        $queue.dequeue();
    });
}).queue(function(){
    $.get('ajax/test2.html', function(data) {
        $queue.dequeue();
    });
}).queue(function(){
    $.get('ajax/test3.html', function(data) {
        $queue.dequeue();
    });
}).queue(function(){
    $.get('ajax/test4.html', function(data) {
        $queue.dequeue();
    });
});
20
BBonifield

Utilisez l'événement ajaxStop.

Par exemple, supposons que vous ayez un message en cours de chargement ... lors de l'extraction de 100 demandes ajax et que vous souhaitiez masquer ce message une fois chargé.

De la jQuery doc :

$("#loading").ajaxStop(function() {
  $(this).hide();
});

Notez qu'il attendra que toutes les requêtes ajax soient effectuées sur cette page.

20
olafure

Une solution de contournement ressemble à ceci:

// Define how many Ajax calls must be done
var ajaxCalls = 3;
var counter = 0;
var ajaxCallComplete = function() {
    counter++;
    if( counter >= ajaxCalls ) {
            // When all ajax calls has been done
        // Do something like hide waiting images, or any else function call
        $('*').css('cursor', 'auto');
    }
};

var loadPersons = function() {
        // Show waiting image, or something else
    $('*').css('cursor', 'wait');

    var url = global.ctx + '/loadPersons';
    $.getJSON(url, function(data) {
            // Fun things
    })
    .complete(function() { **ajaxCallComplete();** });
};

var loadCountries = function() {
    // Do things
    var url = global.ctx + '/loadCountries';
    $.getJSON(url, function(data) {
            // Travels
    })
    .complete(function() { **ajaxCallComplete();** });
};

var loadCities = function() {
    // Do things
    var url = global.ctx + '/loadCities';
    $.getJSON(url, function(data) {
            // Travels
    })
    .complete(function() { **ajaxCallComplete();** });
};

$(document).ready(function(){
    loadPersons();
    loadCountries();
    loadCities();
});

L'espoir peut être utile ...

7
Jesfre

javascript est basé sur les événements, vous ne devriez donc jamais attendre , définissez plutôt les hooks/callbacks

Vous pouvez probablement simplement utiliser les méthodes de réussite/complète de jquery.ajax

Ou vous pouvez utiliser . AjaxComplete :

$('.log').ajaxComplete(function(e, xhr, settings) {
  if (settings.url == 'ajax/test.html') {
    $(this).text('Triggered ajaxComplete handler.');
    //and you can do whatever other processing here, including calling another function...
  }
});

bien que vous deviez poster un pseudocode indiquant comment votre (vos) demande (s) ajax est (sont) appelée (s) pour être plus précise ...

7
Stefano

Comme indiqué dans les autres réponses, vous pouvez utiliser ajaxStop() pour attendre que toutes les demandes ajax soient terminées.

$(document).ajaxStop(function() {
     // This function will be triggered every time any ajax request is requested and completed
});

Si vous voulez le faire pour une demande spécifique ajax(), le mieux que vous puissiez faire est d'utiliser la méthode complete() à l'intérieur de certaines requêtes ajax:

$.ajax({
    type: "POST",
    url: "someUrl",
    success: function(data) {
        // This function will be triggered when ajax returns a 200 status code (success)
    },
    complete: function() {
        // This function will be triggered always, when ajax request is completed, even it fails/returns other status code
    },
    error: function() {
        // This will be triggered when ajax request fail.
    }
});


Mais, si vous n’avez besoin d’attendre que quelques requêtes ajax? Utilisez le merveilleux javascript promesses d’attendre jusqu'à ce que ces ajax que vous voulez attendre soient terminés. J'ai brièvement créé un exemple facile et lisible pour vous montrer comment fonctionne promesse avec ajax.
Veuillez vous reporter à l'exemple suivant . J'ai utilisé setTimeout pour clarifier l'exemple.

// Note:
// resolve() is used to mark the promise as resolved
// reject() is used to mark the promise as rejected

$(document).ready(function() {
    $("button").on("click", function() {

        var ajax1 = new Promise((resolve, reject) => {
            $.ajax({
                type: "GET",
                url: "https://miro.medium.com/max/1200/0*UEtwA2ask7vQYW06.png",
                xhrFields: { responseType: 'blob'},
                success: function(data) {
                    setTimeout(function() {
                        $('#image1').attr("src", window.URL.createObjectURL(data));
                        resolve(" Promise ajax1 resolved");
                    }, 1000);
                },
                error: function() {
                    reject(" Promise ajax1 rejected");
                },
            });
        });

        var ajax2 = new Promise((resolve, reject) => {
            $.ajax({
                type: "GET",
                url: "https://cdn1.iconfinder.com/data/icons/social-media-vol-1-1/24/_github-512.png",
                xhrFields: { responseType: 'blob' },
                success: function(data) {
                    setTimeout(function() {
                         $('#image2').attr("src", window.URL.createObjectURL(data));
                         resolve(" Promise ajax2 resolved");
                    }, 1500);
                },
                error: function() {
                    reject(" Promise ajax2 rejected");
                },
            });
        });

        var ajax3 = new Promise((resolve, reject) => {
            $.ajax({
                type: "GET",
                url: "https://miro.medium.com/max/632/1*LUfpOf7teWvPdIPTBmYciA.png",
                xhrFields: { responseType: 'blob' },
                success: function(data) {
                    setTimeout(function() {
                         $('#image3').attr("src", window.URL.createObjectURL(data));
                         resolve(" Promise ajax3 resolved");
                    }, 2000);
                },
                error: function() {
                    reject(" Promise ajax3 rejected");
                },
            });
        });
        
        Promise.all([ajax1, ajax2, ajax3]).then(values => {
            console.log("We waited until ajax ended: " + values);
            console.log("My few ajax ended, lets do some things!!")
        }, reason => {
            console.log("Promises failed: " + reason);
        });
        
        // Or if you want wait for them individually do it like this
        // ajax1.then(values => {
        //    console.log("Promise 1 resolved: " + values)
        // }, reason => {
        //     console.log("Promise 1 failed: " + reason)
        // });
    });

});
img {
  max-width: 200px;
  max-height: 100px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button>Make AJAX request</button>
<div id="newContent">
    <img id="image1" src="">
    <img id="image2" src="">
    <img id="image3" src="">
</div>
2
SilverSurfer

jQuery vous permet de spécifier si vous souhaitez que la demande ajax soit asynchrone ou non. Vous pouvez simplement rendre les demandes ajax synchrones et le reste du code ne sera pas exécuté avant leur retour.

Par exemple:

jQuery.ajax({ 
    async: false,
    //code
});
2
shmuel613

Si vous avez besoin de quelque chose de simple rappel une fois fait

        //multiple ajax calls above
        var callback = function () {
            if ($.active !== 0) {
                setTimeout(callback, '500');
                return;
            }
            //whatever you need to do here
            //...
        };
        callback();
1
Fatmuemoo

Pour développer la réponse d'Alex, j'ai un exemple avec des arguments et des promesses variables. Je voulais charger des images via ajax et les afficher sur la page après leur chargement.

Pour ce faire, j'ai utilisé ce qui suit:

let urlCreator = window.URL || window.webkitURL;

// Helper function for making ajax requests
let fetch = function(url) {
    return $.ajax({
        type: "get",
        xhrFields: {
            responseType: "blob"
        },
        url: url,
    });
};

// Map the array of urls to an array of ajax requests
let urls = ["https://placekitten.com/200/250", "https://placekitten.com/300/250"];
let files = urls.map(url => fetch(url));

// Use the spread operator to wait for all requests
$.when(...files).then(function() {
    // If we have multiple urls, then loop through
    if(urls.length > 1) {
        // Create image urls and tags for each result
        Array.from(arguments).forEach(data => {
            let imageUrl = urlCreator.createObjectURL(data[0]);
            let img = `<img src=${imageUrl}>`;
            $("#image_container").append(img);
        });
    }
    else {
        // Create image source and tag for result
        let imageUrl = urlCreator.createObjectURL(arguments[0]);
        let img = `<img src=${imageUrl}>`;
        $("#image_container").append(img);
    }
});

Mise à jour pour fonctionner avec une ou plusieurs URL: https://jsfiddle.net/euypj5w9/

1
GammaGames

J'utilise la vérification de la taille lorsque tous les travaux ajax sont terminés

function get_ajax(link, data, callback) {
    $.ajax({
        url: link,
        type: "GET",
        data: data,
        dataType: "json",
        success: function (data, status, jqXHR) {
            callback(jqXHR.status, data)
        },
        error: function (jqXHR, status, err) {
            callback(jqXHR.status, jqXHR);
        },
        complete: function (jqXHR, status) {
        }
    })
}

function run_list_ajax(callback){
    var size=0;
    var max= 10;
    for (let index = 0; index < max; index++) {
        var link = 'http://api.jquery.com/ajaxStop/';
        var data={i:index}
        get_ajax(link,data,function(status, data){
            console.log(index)
            if(size>max-2){
                callback('done')
            }
            size++
            
        })
    }
}

run_list_ajax(function(info){
    console.log(info)
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
1
tranchau

$.when ne fonctionne pas pour moi, callback(x) au lieu de return x a fonctionné comme décrit ici: https://stackoverflow.com/a/13455253/10357604

1

Sur la base de la réponse @BBonifield, j'ai écrit une fonction utilitaire afin que la logique de sémaphore ne soit pas étendue à tous les appels ajax.

untilAjax est la fonction utilitaire qui appelle une fonction de rappel lorsque tous les ajaxCalls sont terminés.

ajaxObjs est un tableau d'objets de réglage ajax [http://api.jquery.com/jQuery.ajax/].

fn est une fonction de rappel

function untilAjax(ajaxObjs, fn) {
  if (!ajaxObjs || !fn) {
    return;
  }
  var ajaxCount = ajaxObjs.length,
    succ = null;

  for (var i = 0; i < ajaxObjs.length; i++) { //append logic to invoke callback function once all the ajax calls are completed, in success handler.
    succ = ajaxObjs[i]['success'];
    ajaxObjs[i]['success'] = function(data) { //modified success handler
      if (succ) {
        succ(data);
      }
      ajaxCount--;
      if (ajaxCount == 0) {
        fn(); //modify statement suitably if you want 'this' keyword to refer to another object
      }
    };
    $.ajax(ajaxObjs[i]); //make ajax call
    succ = null;
  };

Exemple: la fonction doSomething utilise untilAjax.

function doSomething() {
  // variable declarations
  untilAjax([{
    url: 'url2',
    dataType: 'json',
    success: function(data) {
      //do something with success data
    }
  }, {
    url: 'url1',
    dataType: 'json',
    success: function(data) {
      //do something with success data
    }
  }, {
    url: 'url2',
    dataType: 'json',
    success: function(response) {
      //do something with success data
    }
  }], function() {
    // logic after all the calls are completed.
  });
}
1
Sanjeev Kumar Dangi

Aussi, vous pouvez utiliser async.js .

Je pense que c'est mieux que $ .when, car vous pouvez fusionner toutes sortes d'appels asynchrones qui ne prennent pas en charge les promesses prêtes à l'emploi, comme les délais d'attente, les appels SqlLite, etc., et pas seulement les demandes ajax.

1
George Mavritsakis

Je recommande fortement d'utiliser $. When () si vous partez de zéro.

Même si cette question a plus de millions de réponses, je n'ai toujours rien trouvé d'utile pour mon cas. Supposons que vous ayez à traiter avec une base de code existante, que vous passiez déjà quelques appels ajax et que vous ne vouliez pas introduire la complexité des promesses et/ou refaire le tout.

Nous pouvons facilement tirer parti des fonctions jQuery .data, .on et .trigger qui font partie de jQuery depuis toujours.

Codepen

Les bonnes choses à propos de ma solution sont:

  • il est évident que le rappel dépend exactement

  • la fonction triggerNowOrOnLoaded importe peu si les données ont déjà été chargées ou si nous les attendons toujours

  • c'est super facile de le brancher sur un code existant

$(function() {

  // wait for posts to be loaded
  triggerNowOrOnLoaded("posts", function() {
    var $body = $("body");
    var posts = $body.data("posts");

    $body.append("<div>Posts: " + posts.length + "</div>");
  });


  // some ajax requests
  $.getJSON("https://jsonplaceholder.typicode.com/posts", function(data) {
    $("body").data("posts", data).trigger("posts");
  });

  // doesn't matter if the `triggerNowOrOnLoaded` is called after or before the actual requests 
  $.getJSON("https://jsonplaceholder.typicode.com/users", function(data) {
    $("body").data("users", data).trigger("users");
  });


  // wait for both types
  triggerNowOrOnLoaded(["posts", "users"], function() {
    var $body = $("body");
    var posts = $body.data("posts");
    var users = $body.data("users");

    $body.append("<div>Posts: " + posts.length + " and Users: " + users.length + "</div>");
  });

  // works even if everything has already loaded!
  setTimeout(function() {

    // triggers immediately since users have been already loaded
    triggerNowOrOnLoaded("users", function() {
      var $body = $("body");
      var users = $body.data("users");

      $body.append("<div>Delayed Users: " + users.length + "</div>");
    });

  }, 2000); // 2 seconds

});

// helper function
function triggerNowOrOnLoaded(types, callback) {
  types = $.isArray(types) ? types : [types];

  var $body = $("body");

  var waitForTypes = [];
  $.each(types, function(i, type) {

    if (typeof $body.data(type) === 'undefined') {
      waitForTypes.Push(type);
    }
  });

  var isDataReady = waitForTypes.length === 0;
  if (isDataReady) {
    callback();
    return;
  }

  // wait for the last type and run this function again for the rest of the types
  var waitFor = waitForTypes.pop();
  $body.on(waitFor, function() {
    // remove event handler - we only want the stuff triggered once
    $body.off(waitFor);

    triggerNowOrOnLoaded(waitForTypes, callback);
  });
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<body>Hi!</body>
1
cilf

Ma solution est la suivante

var request;
...
'services': {
  'GetAddressBookData': function() {
    //This is the primary service that loads all addressbook records 
    request = $.ajax({
      type: "POST",
      url: "Default.aspx/GetAddressBook",
      contentType: "application/json;",
      dataType: "json"
    });
  },

  ...

  'apps': {
    'AddressBook': {
      'data': "",
      'Start': function() {
          ...services.GetAddressBookData();
          request.done(function(response) {
            trace("ajax successful");
            ..apps.AddressBook.data = response['d'];
            ...apps.AddressBook.Filter();
          });
          request.fail(function(xhr, textStatus, errorThrown) {
            trace("ajax failed - " + errorThrown);
          });

A fonctionné assez bien. J'ai essayé beaucoup de façons différentes de le faire, mais j'ai trouvé que c'était la plus simple et la plus réutilisable. J'espère que ça aide

0
Eric Sean Moore

J'ai trouvé un moyen simple, en utilisant shift()

function waitReq(id)
{
  jQuery.ajax(
  {
    type: 'POST',
    url: ajaxurl,
    data:
    {
      "page": id
    },
    success: function(resp)
    {
      ...........
      // check array length if not "0" continue to use next array value
      if(ids.length)
      {
        waitReq(ids.shift()); // 2
      )
    },
    error: function(resp)
    {
      ....................
      if(ids.length)
      {
        waitReq(ids.shift());
      )
    }
  });
}

var ids = [1, 2, 3, 4, 5];    
// shift() = delete first array value (then print)
waitReq(ids.shift()); // print 1
0
uingtea

Regardez ma solution:

1.Insérez cette fonction (et cette variable) dans votre fichier javascript:

var runFunctionQueue_callback;

function runFunctionQueue(f, index, callback) {

  var next_index = index + 1

  if (callback !== undefined) runFunctionQueue_callback = callback;

  if (f[next_index] !== undefined) {
    console.log(index + ' Next function avalaible -> ' + next_index);
    $.ajax({
      type: 'GET',
      url: f[index].file,
      data: (f[index].data),
      complete: function() {
        runFunctionQueue(f, next_index);
      }
    });
  } else {
    console.log(index + ' Last function');
    $.ajax({
      type: 'GET',
      url: f[index].file,
      data: (f[index].data),
      async: false,
      complete: runFunctionQueue_callback
    });
  }
}

2.Buil un tableau avec vos demandes, comme ceci:

var f = [
           {file: 'file_path', data: {action: 'action', data: 'any_data}},
           {file: 'file_path', data: {action: 'action', data: 'any_data}},
           {file: 'file_path', data: {action: 'action', data: 'any_data}},
           {file: 'file_path', data: {action: 'action', data: 'any_data}}
        ];

Fonction de rappel 3.Create:

function Function_callback() {
  alert('done');
}

4. Appelez la fonction runFunctionQueue avec les paramètres:

runFunctionQueue(f, 0, QuestionInsert_callback);
// first parameter: array with requests data
// second parameter: start from first request
// third parameter: the callback function
0
Briganti

J'ai rencontré ce problème et créé un plugin générique jquery_counter pour le résoudre: https://bitbucket.org/stxnext/jquery_counter/

0
zefciu