Jak sprawić, by funkcja czekała, aż wszystkie żądania Ajax jQuery zostaną wykonane w innej funkcji?
Krótko mówiąc, muszę wykonać wszystkie żądania Ajax, zanim wykonam następne. Ale jak?
Jak sprawić, by funkcja czekała, aż wszystkie żądania Ajax jQuery zostaną wykonane w innej funkcji?
Krótko mówiąc, muszę wykonać wszystkie żądania Ajax, zanim wykonam następne. Ale jak?
Odpowiedzi:
W tym celu jQuery definiuje teraz funkcję when .
Akceptuje dowolną liczbę Odroczonych obiektów jako argumenty i wykonuje funkcję, gdy wszystkie z nich zostaną rozpoznane.
Oznacza to, że jeśli chcesz zainicjować (na przykład) cztery żądania ajax, a następnie wykonać akcję po ich zakończeniu, możesz zrobić coś takiego:
$.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,
...
});
}
Moim zdaniem zapewnia czystą i przejrzystą składnię i pozwala uniknąć zmiennych globalnych, takich jak ajaxStart i ajaxStop, które mogłyby mieć niepożądane skutki uboczne w miarę rozwoju twojej strony.
Jeśli nie wiesz z góry, na ile argumentów ajax musisz czekać (tzn. Chcesz użyć zmiennej liczby argumentów), możesz to nadal zrobić, ale jest to trochę trudniejsze. Zobacz Przekaż w tablicy Odroczone do $ .when () (i być może jQuery .w przypadku rozwiązywania problemów przy zmiennej liczbie argumentów ).
Jeśli potrzebujesz głębszej kontroli nad trybami błędów skryptów ajax itp., Możesz zapisać zwrócony obiekt .when()
- jest to obiekt obietnicy jQuery obejmujący wszystkie oryginalne zapytania ajax. Możesz do niego zadzwonić .then()
lub .fail()
dodać szczegółowe procedury obsługi sukcesu / niepowodzenia.
$.when
zwraca Promise
obiekt, który ma nie tylko bardziej użyteczne metody .done
. Na przykład za pomocą .then(onSuccess, onFailure)
metody można zareagować, gdy oba żądania zakończą się powodzeniem lub co najmniej jedno z nich zakończy się niepowodzeniem.
fail
sprawą. W przeciwieństwie do done
, fail
strzela natychmiast po pierwszej porażce i ignoruje pozostałe odroczenia.
onFailure
można dołączyć funkcję. Jak zauważyłem w komentarzu do pytania PO: może on chcieć bardziej precyzyjnie wskazać, co rozumie przez „zrobione”. „Ryan Mohr” miał również bardzo dobrą opinię na temat tego, że fail
zachowuje się inaczej, ponieważ done
kilka dalszych lektur, które należy zrobić, Promises
to chyba html5rocks.com/en/tutorials/es6/promises
Jeśli chcesz wiedzieć, kiedy wszystkie ajax
żądania zostaną zakończone w twoim dokumencie, bez względu na to, ile ich istnieje, po prostu użyj zdarzenia $ .ajaxStop w ten sposób:
$(document).ajaxStop(function () {
// 0 === $.active
});
W takim przypadku nie musisz zgadywać, ile żądań dzieje się w aplikacji, które mogą zakończyć się w przyszłości, ani nie zagłębić się w skomplikowaną logikę funkcji lub znaleźć, które funkcje wykonują
HTTP(S)
żądania.
$.ajaxStop
tutaj można również powiązać z dowolnymHTML
węzłem, który Twoim zdaniem może zostać zmodyfikowany przez żądanie.
Aktualizacja:
jeśli chcesz trzymać się ES
składni, możesz użyć Promise.all dla znanych ajax
metod:
Promise.all([ajax1(), ajax2()]).then(() => {
// all requests finished successfully
}).catch(() => {
// all requests finished but one or more failed
})
Interesującym punktem jest to, że działa zarówno z, jak Promises
i $.ajax
żądaniami.
Oto demonstracja jsFiddle .
Aktualizacja 2:
Jeszcze nowsza wersja wykorzystująca składnię asynchroniczną / oczekującą :
try {
const results = await Promise.all([ajax1(), ajax2()])
// do other actions
} catch(ex) { }
Znalazłem dobrą odpowiedź od gnarfa, której właśnie szukałem :)
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);
Następnie możesz dodać do kolejki zapytanie ajax w następujący sposób:
$.ajaxQueue({
url: 'page.php',
data: {id: 1},
type: 'POST',
success: function(data) {
$('#status').html(data);
}
});
Wykorzystaj ajaxStop
wydarzenie.
Załóżmy na przykład, że masz komunikat ładujący ... podczas pobierania 100 żądań ajax i chcesz ukryć tę wiadomość po załadowaniu.
Z dokumentu jQuery :
$("#loading").ajaxStop(function() {
$(this).hide();
});
Pamiętaj, że będzie czekać na wszystkie żądania ajax wykonane na tej stronie.
UWAGA: Powyższe odpowiedzi wykorzystują funkcjonalność, która nie istniała w chwili pisania tej odpowiedzi. Zalecam stosowanie jQuery.when()
zamiast tych metod, ale pozostawiam odpowiedź do celów historycznych.
-
Prawdopodobnie możesz sobie poradzić z prostym semaforem liczącym, chociaż sposób jego implementacji będzie zależał od twojego kodu. Prostym przykładem byłoby coś takiego ...
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;
Jeśli chcesz, aby działało to jak {async: false}, ale nie chciałeś blokować przeglądarki, możesz to samo zrobić z kolejką 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();
});
});
.get()
. W ten sposób przynajmniej nie powielasz tego kodu. Nie tylko to, ale deklarowanie za function(){}
każdym razem przydziela pamięć za każdym razem! Raczej zła praktyka, jeśli można wywołać funkcję zdefiniowaną statycznie.
javascript jest oparty na zdarzeniach, więc nigdy nie powinieneś czekać , raczej ustaw haki / callbacki
Prawdopodobnie możesz po prostu użyć powodzenia / kompletnych metod jquery.ajax
Lub możesz użyć .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...
}
});
chociaż powinieneś opublikować pseudokod, w jaki sposób twoje (a) żądania (a) ajaxowe są (są) wywoływane, aby być bardziej precyzyjnym ...
Trochę tego obejścia:
// 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();
});
Nadzieja może się przydać ...
jQuery pozwala określić, czy chcesz, aby żądanie ajax było asynchroniczne, czy nie. Możesz po prostu zsynchronizować żądania ajax, a wtedy reszta kodu nie zostanie wykonana, dopóki nie zostaną zwrócone.
Na przykład:
jQuery.ajax({
async: false,
//code
});
Jeśli potrzebujesz czegoś prostego; raz i gotowe oddzwonienie
//multiple ajax calls above
var callback = function () {
if ($.active !== 0) {
setTimeout(callback, '500');
return;
}
//whatever you need to do here
//...
};
callback();
Możesz także użyć async.js .
Myślę, że jest lepszy niż $ .when, ponieważ można scalić wszystkie rodzaje wywołań asynchronicznych, które nie obsługują obietnic po wyjęciu z pudełka, takich jak limity czasu, wywołania SqlLite itp., A nie tylko żądania ajax.
Na podstawie odpowiedzi @BBonifield napisałem funkcję narzędzia, dzięki której logika semaforów nie rozprzestrzenia się we wszystkich wywołaniach ajax.
untilAjax
to funkcja narzędziowa, która wywołuje funkcję zwrotną po zakończeniu wszystkich wywołań ajaxCall.
ajaxObjs
jest tablicą obiektów ustawień ajax [http://api.jquery.com/jQuery.ajax/]
.
fn
jest funkcją oddzwaniania
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;
};
Przykład: doSomething
zastosowania funkcji 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.
});
}
Zdecydowanie polecam użycie $ .when (), jeśli zaczynasz od zera.
Mimo że to pytanie ma ponad milion odpowiedzi, nadal nie znalazłem nic przydatnego w mojej sprawie. Powiedzmy, że masz do czynienia z istniejącą bazą kodów, już wykonując kilka wywołań ajax i nie chcesz wprowadzać złożoności obietnic i / lub przerobić całą sprawę.
Możemy łatwo wykorzystać jQuery .data
, .on
a .trigger
funkcje, które były częścią jQuery od zawsze.
Dobra rzecz w moim rozwiązaniu to:
oczywiste jest, na czym dokładnie zależy oddzwonienie
funkcja triggerNowOrOnLoaded
nie dba o to, czy dane zostały już załadowane, czy nadal na nie czekamy
bardzo łatwo jest podłączyć go do istniejącego kodu
$(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>
Używam sprawdzania rozmiaru po zakończeniu ładowania całego ajax
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>
Aby rozwinąć odpowiedź Alexa, mam przykład ze zmiennymi argumentami i obietnicami. Chciałem załadować obrazy za pośrednictwem ajax i wyświetlić je na stronie po ich załadowaniu.
Aby to zrobić, użyłem następującego:
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);
}
});
Zaktualizowany do pracy z jednym lub wieloma adresami URL: https://jsfiddle.net/euypj5w9/
Jak wspomniano w innych odpowiedziach, możesz ajaxStop()
poczekać, aż wszystkie żądania ajax zostaną zakończone.
$(document).ajaxStop(function() {
// This function will be triggered every time any ajax request is requested and completed
});
Jeśli chcesz to zrobić dla konkretnego ajax()
żądania, najlepiej możesz użyć complete()
metody wewnątrz określonego żądania 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.
}
});
Ale jeśli musisz poczekać tylko na kilka i pewne żądania ajax, które mają zostać wykonane? Skorzystaj ze wspaniałych obietnic javascript, aby poczekać, aż te aukcje, na które chcesz czekać, zostaną zakończone. Zrobiłem krótki, łatwy i czytelny przykład, aby pokazać, jak obietnice działają z ajaxem.
Proszę spojrzeć na następny przykład . Użyłem setTimeout
do wyjaśnienia przykład.
// 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>
Znalazłem prosty sposób, używając 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
Moje rozwiązanie jest następujące
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);
});
Działało całkiem nieźle. Próbowałem wielu różnych sposobów na zrobienie tego, ale okazało się, że jest to najprostszy i najbardziej wielokrotnego użytku. Mam nadzieję, że to pomoże
Spójrz na moje rozwiązanie:
1.Włóż tę funkcję (i zmienną) do pliku 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.Zbuduj tablicę ze swoimi żądaniami:
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}}
];
3. Utwórz funkcję oddzwaniania:
function Function_callback() {
alert('done');
}
4. Wywołaj funkcję runFunctionQueue z parametrami:
runFunctionQueue(f, 0, QuestionInsert_callback);
// first parameter: array with requests data
// second parameter: start from first request
// third parameter: the callback function
$.when
nie działa dla mnie, callback(x)
zamiast return x
działać zgodnie z opisem tutaj: https://stackoverflow.com/a/13455253/10357604
Wypróbuj w ten sposób utwórz pętlę wewnątrz funkcji skryptu Java, aby poczekać, aż zakończy się wywołanie ajax.
function getLabelById(id)
{
var label = '';
var done = false;
$.ajax({
cache: false,
url: "YourMvcActionUrl",
type: "GET",
dataType: "json",
async: false,
error: function (result) {
label='undefined';
done = true;
},
success: function (result) {
label = result.Message;
done = true;
}
});
//A loop to check done if ajax call is done.
while (!done)
{
setTimeout(function(){ },500); // take a sleep.
}
return label;
}
setTimeout()
NIE take a sleep
. W takim przypadku po prostu blokujesz wszystkie karty, aż stanie done
się prawdą.
done
nigdy nie będzie to prawdą, dopóki pętla while będzie nadal działać. Jeśli pętla while jest uruchomiona, pętla zdarzeń nie może być kontynuowana i dlatego nigdy nie uruchomi wywołania zwrotnego do sukcesu ajax.