javascript - Vent til alle jQuery Ajax-forespørsler er ferdige?


Hvordan får jeg en funksjon til å vente til alle jQuery Ajax-forespørsler er ferdige inne i en annen funksjon?

Kort sagt, jeg må vente på at alle Ajax-forespørsler er ferdige før jeg utfører neste. Men hvordan?

Svar #1

jQuery definerer nå en when-funksjon for dette formålet.

Den aksepterer et hvilket som helst antall Deferred-objekter som argumenter, og utfører en funksjon når alle av dem fullføres.

Det betyr at hvis du vil starte (for eksempel) fire AJAX-forespørsler, og deretter utføre en handling når de er ferdige, kan du gjøre noe sånt som dette:

$.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,            
        ...
    });
}

Etter min mening gir det en ren og klar syntaks, og unngår å involvere globale variabler som ajaxStart og ajaxStop, som kan ha uønskede bivirkninger etter hvert som siden din utvikler seg.

Hvis du ikke vet på forhånd hvor mange ajax-argumenter du må vente på (dvs. du vil bruke et variabelt antall argumenter), kan det fortsatt gjøres, men det er bare litt mer komplisert. Se Send inn et array av Deferreds til $.when() (og kanskje jQuery .when feilsøking med variabelt antall argumenter).

Hvis du trenger dypere kontroll over feilmønstrene til ajax-skriptene osv., kan du lagre objektet som returneres av .when() - det er et jQuery Promise-objekt som omfatter alle de originale ajax-forespørslene. Du kan kalle .then() eller .fail() på det for å legge til detaljerte suksess-/feilhåndterere.

Svar #2

Hvis du vil vite når alle ajax-forespørsler er ferdige i dokumentet ditt, uansett hvor mange det finnes, bruk $.ajaxStop-hendelsen på denne måten:

$(document).ajaxStop(function () {
  // 0 === $.active
  $(this).unbind('ajaxStop'); // to stop this event repeating further
});

I dette tilfellet trenger du verken å gjette hvor mange forespørsler som skjer i applikasjonen som kan fullføres i fremtiden, eller grave deg inn i funksjoners komplekse logikk eller finne hvilke funksjoner som gjør HTTP(S)-forespørsler.

$.ajaxStop her kan også knyttes til hvilken som helst HTML-node som du tror kan bli endret av forespørsel.


Oppdatering:
Hvis du vil holde deg til ES-syntaks, kan du bruke Promise.all for kjente ajax-metoder:

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

Interessant nok fungerer det med Promises så vel som $.ajax-forespørsler.

Her er jsFiddle-demonstrasjonen.


Oppdatering 2:
En enda nyere versjon som bruker async/await-syntaks:

try {
  const results = await Promise.all([ajax1(), ajax2()])
  // do other actions
} catch(ex) { }

Svar #3

Jeg fant et godt svar av gnarf selv som er akkurat det jeg lette etter :)

jQuery ajaxKø

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

Da kan du legge til en AJAX-forespørsel i køen på denne måten:

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

Svar #4

MERK: Svarene ovenfor bruker funksjonalitet som ikke eksisterte på det tidspunktet dette svaret ble skrevet. Jeg anbefaler å bruke jQuery.when() i stedet for disse tilnærmingene, men jeg lar svaret stå for historiske formål.

-

Du kunne sannsynligvis klare deg med en enkel teller-semafor, selv om hvordan du implementerer den ville avhenge av koden din. Et enkelt eksempel ville være noe som...

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;

Hvis du ønsket at dette skulle fungere som {async: false}, men du ikke ville låse nettleseren, kunne du oppnå det samme med en jQuery-kø.

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

Svar #5

Bruk ajaxStop-hendelsen.

For eksempel, la oss si at du har en laster ... melding mens du henter 100 ajax-forespørsler, og du vil skjule den meldingen når den er lastet.

Fra jQuery-dokumentasjonen:

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

Vær oppmerksom på at det vil vente på at alle ajax-forespørsler på den siden blir ferdige.

Svar #6

En liten løsning er noe sånt som dette:

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

Håp kan være nyttig...

Svar #7

jQuery lar deg spesifisere om du vil at ajax-forespørselen skal være asynkron eller ikke. Du kan enkelt gjøre ajax-forespørslene synkrone, og da vil ikke resten av koden kjøre før de returnerer.

For eksempel:

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

Relaterte artikler