Pregunta Espera hasta que todas las solicitudes jQuery Ajax hayan terminado.


¿Cómo hago para que una función espere hasta que todas las solicitudes jQuery Ajax se realicen dentro de otra función?

En resumen, tengo que esperar a que se realicen todas las solicitudes de Ajax antes de ejecutar el siguiente. ¿Pero cómo?


562
2017-09-14 14:02


origen


Respuestas:


jQuery ahora define un cuando la función para este propósito.

Acepta cualquier número de objetos Diferidos como argumentos y ejecuta una función cuando todos se resuelven.

Eso significa que, si desea iniciar (por ejemplo) cuatro solicitudes ajax, luego realice una acción cuando hayan terminado, podría hacer algo como esto:

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

En mi opinión, crea una sintaxis limpia y clara, y evita la participación de variables globales como ajaxStart y ajaxStop, que podrían tener efectos secundarios no deseados a medida que su página se desarrolle.

Si no sabe de antemano cuántos argumentos Ajax necesita esperar (es decir, si desea usar una cantidad variable de argumentos), aún puede hacerse, pero es un poco más complicado. Ver Pase una variedad de Deferreds a $ .when () (y tal vez jQuery. cuando soluciona problemas con una cantidad variable de argumentos)

Si necesita un control más profundo sobre los modos de falla de los scripts de Ajax, etc., puede guardar el objeto devuelto por .when() - es un jQuery Promesa objeto que abarca todas las consultas ajax originales. Puedes llamar .then() o .fail() para agregar controladores detallados de éxito / falla.


761
2018-03-25 23:54



Si desea esperar hasta que todas las solicitudes ajax hayan finalizado en su documento, no importa cuántas de ellas existan, solo use $.ajaxStop evento de esta manera:

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

En este caso, no hay necesidad de adivinar cuántas solicitudes puede haber en una aplicación que podría finalizar en el futuro. En algunos casos, las solicitudes ajax pueden ser parte de la lógica interna de una función, lo que puede ser bastante complicado (por ejemplo, llamar a otras funciones) y, en ese caso, es posible que no espere hasta que dicha función se realice con toda su lógica en lugar de solamente esperando ajax parte para completar.

$.ajaxStop aquí también puede estar vinculado a cualquier HTML nodo que crees que podría ser modificado por ajax.

Una vez más, el propósito de este controlador es saber cuándo no hay activo  ajax no borrar o restablecer algo

PD Si no te importa usar la sintaxis de ES6, puedes usar Promise.all para sabido ajax métodos. Ejemplo:

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

Un punto interesante aquí es que funciona tanto con Promises y $.ajax peticiones. Aquí está jsFiddle demostrando el último.


254
2017-12-26 08:42



Encontre un buena respuesta por gnarf mi yo, que es exactamente lo que estaba buscando :)

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

Luego puede agregar una solicitud de Ajax a la cola de esta manera:

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

29
2017-09-14 15:26



NOTA: Las respuestas anteriores usan una funcionalidad que no existía en el momento en que se escribió esta respuesta. Recomiendo usar jQuery.when() en lugar de estos enfoques, pero dejo la respuesta para propósitos históricos.

-

Es probable que puedas salir adelante con un simple semáforo de conteo, aunque la forma de implementarlo dependería de tu código. Un ejemplo simple sería algo así como ...

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 desea que esto funcione como {async: falso} pero no desea bloquear el navegador, puede lograr lo mismo con una cola 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
2017-09-14 14:26



Utilizar el ajaxStop evento.

Por ejemplo, digamos que tienes un cargando ... mensaje mientras obtiene 100 solicitudes ajax y desea ocultar ese mensaje una vez cargado.

Desde el jQuery doc:

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

Tenga en cuenta que esperará a que se realicen todas las solicitudes ajax en esa página.


19
2018-05-24 11:42



Una pequeña solución es algo como esto:

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

La esperanza puede ser útil ...


7
2017-09-14 15:07



JavaScript está basado en eventos, por lo que nunca deberías Espere, en lugar de establecer ganchos / devoluciones de llamada

Probablemente puedas simplemente usar los métodos de éxito / completar jquery.ajax

O podrías usar .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...
  }
});

aunque deberías publicar un pseudocódigo de cómo se (n) llama (n) tu (s) solicitud (es) ajax para que sean más precisas ...


6
2017-09-14 14:27



jQuery le permite especificar si desea que la solicitud de ajax sea asíncrona o no. Simplemente puede hacer que las solicitudes ajax sean síncronas y luego el resto del código no se ejecutará hasta que vuelvan.

Por ejemplo:

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

2
2018-02-19 15:33



Sobre la base de la respuesta de @BBonifield, escribí una función de utilidad para que la lógica del semáforo no se distribuya en todas las llamadas ajax.

untilAjax es la función de utilidad que invoca una función de devolución de llamada cuando todas las ajaxCalls se completan.

ajaxObjs es una matriz de objetos de configuración ajax [http://api.jquery.com/jQuery.ajax/].

fn es la función de devolución de llamada

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

Ejemplo: doSomething función utiliza 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
2017-08-28 11:15



Recomiendo usar $ .when () si estás empezando desde cero.

Aunque esta pregunta tiene más de un millón de respuestas, todavía no encontré nada útil para mi caso. Supongamos que tiene que tratar con una base de código existente, ya está haciendo algunas llamadas ajax y no desea introducir la complejidad de las promesas y / o rehacer todo.

Podemos aprovechar fácilmente jQuery .data, .on y .trigger funciones que han sido parte de jQuery desde siempre.

Codepen

Lo bueno de mi solución es:

  • es obvio de lo que depende exactamente la devolución de llamada

  • la función triggerNowOrOnLoaded no importa si los datos ya se han cargado o aún estamos esperando

  • es muy fácil conectarlo a un código existente

$(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
2018-05-21 14:03



Si necesitas algo simple; una vez y hecho devolución de llamada

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

0
2018-06-14 07:38