Як змусити функцію чекати, поки всі запити jQuery Ajax будуть виконані всередині іншої функції?
Коротше кажучи, мені потрібно дочекатися, коли всі запити Ajax будуть виконані, перш ніж виконати наступний. Але як?
Як змусити функцію чекати, поки всі запити jQuery Ajax будуть виконані всередині іншої функції?
Коротше кажучи, мені потрібно дочекатися, коли всі запити Ajax будуть виконані, перш ніж виконати наступний. Але як?
Відповіді:
jQuery тепер визначає функцію if для цієї мети.
Він приймає в якості аргументів будь-яку кількість відкладених об'єктів і виконує функцію, коли всі вони вирішуються.
Це означає, що якщо ви хочете ініціювати (наприклад) чотири запити ajax, а потім виконати дію, коли вони виконані, ви можете зробити щось подібне:
$.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,
...
});
}
На мою думку, це створює чистий і чіткий синтаксис і уникає залучення будь-яких глобальних змінних, таких як ajaxStart і ajaxStop, які могли б мати небажані побічні ефекти в міру розвитку вашої сторінки.
Якщо ви не знаєте заздалегідь, скільки аргументів ajax потрібно чекати (тобто ви хочете використовувати змінну кількість аргументів), це все одно можна зробити, але це лише трохи складніше. Див. Перехід у масиві відкладених до $ .when () (і, можливо, jQuery .при усуненні несправностей зі змінною кількістю аргументів ).
Якщо вам потрібен більш глибокий контроль над режимами відмов сценаріїв ajax тощо, ви можете зберегти повернутий об'єкт .when()
- це об’єкт jQuery Promise, що охоплює всі оригінальні запити ajax. Ви можете зателефонувати .then()
або .fail()
на нього, щоб додати детальний обробник успіху / відмови.
$.when
повертає Promise
об'єкт, який має не тільки корисні методи .done
. Наприклад, за допомогою .then(onSuccess, onFailure)
методу ви могли б відреагувати, коли обидва запити вдаються або принаймні один з них не вдається.
fail
справою. На відміну від цього done
, fail
пожежі одразу на перших збоях і ігнорування решти відкладених.
onFailure
функцію можна було приєднати. Як я зазначив у коментарі до питання ОП: він, можливо, захоче точніше вказати, що він мав на увазі під «зробив». «Райан Мор» було також дуже хороший момент про те , що fail
поводиться по- іншому , як done
, деякі подальше читання , щоб бути зроблено , Promises
я думаю , html5rocks.com/en/tutorials/es6/promises
Якщо ви хочете дізнатися, коли всі ajax
запити закінчуються у вашому документі, незалежно від того, скільки їх існує, просто використовуйте $ .ajaxStop подія таким чином:
$(document).ajaxStop(function () {
// 0 === $.active
});
У цьому випадку ні вам не потрібно здогадуватися, скільки запитів відбувається в додатку, які можуть закінчитися в майбутньому, і не занурюватися у функції складної логіки або знаходити, які функції виконують
HTTP(S)
запити.
$.ajaxStop
тут також можна прив’язати будь-якийHTML
вузол, який, на вашу думку, може бути змінений реквестом.
Оновлення:
якщо ви хочете дотримуватися ES
синтаксису, тоді ви можете використовувати Promise.all для відомих ajax
методів:
Promise.all([ajax1(), ajax2()]).then(() => {
// all requests finished successfully
}).catch(() => {
// all requests finished but one or more failed
})
Цікавим моментом тут є те, що він працює і з, Promises
і з$.ajax
запитами, .
Ось демонстрація jsFiddle .
Оновлення 2:
Ще новіша версія з використанням синтаксису async / wait :
try {
const results = await Promise.all([ajax1(), ajax2()])
// do other actions
} catch(ex) { }
Я знайшов хорошу відповідь від gnarf my self, який саме те, що я шукав :)
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);
Тоді ви можете додати запит ajax таким чином:
$.ajaxQueue({
url: 'page.php',
data: {id: 1},
type: 'POST',
success: function(data) {
$('#status').html(data);
}
});
Використовувати ajaxStop
подію.
Наприклад, скажімо, у вас завантаження ... під час отримання 100 запитів ajax, і ви хочете приховати це повідомлення після завантаження.
Від документа jQuery :
$("#loading").ajaxStop(function() {
$(this).hide();
});
Зверніть увагу, що він буде чекати, коли всі запити Ajax будуть виконані на цій сторінці.
ПРИМІТКА. У наведених вище відповідях використовується функціонал, який не існував під час написання цієї відповіді. Я рекомендую використовувати jQuery.when()
замість цих підходів, але відповідь залишаю в історичних цілях.
-
Ви, ймовірно, могли обійтись простим семафором підрахунку, хоча те, як ви реалізуєте це, залежатиме від вашого коду. Простий приклад може бути щось на кшталт ...
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;
Якщо ви хотіли, щоб це працювало на зразок {async: false}, але ви не хотіли заблокувати браузер, ви можете виконати те ж саме за допомогою черги 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()
. Таким чином ви принаймні не дублюєте цей код. Мало того, але декларування function(){}
кожного разу виділяє пам'ять кожного разу! Досить погана практика, якщо ви можете викликати статично визначену функцію.
javascript заснований на подіях, тому вам ніколи не варто чекати , а встановлюйте гачки / зворотні дзвінки
Ви, ймовірно, можете просто скористатися методами успіху / завершення jquery.ajax
Або ви можете використовувати .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...
}
});
хоча ви повинні опублікувати псевдокод того, як ваші (-і) запити (-и) ajax (-и) називаються більш точними ...
Трохи вирішити щось подібне:
// 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();
});
Сподівання може бути корисною ...
jQuery дозволяє вказати, хочете ви, щоб запит ajax був асинхронним чи ні. Ви можете просто зробити запити ajax синхронними, і тоді решта коду не буде виконуватися, поки вони не повернуться.
Наприклад:
jQuery.ajax({
async: false,
//code
});
Якщо вам потрібно щось просте; один раз і готовий зворотний дзвінок
//multiple ajax calls above
var callback = function () {
if ($.active !== 0) {
setTimeout(callback, '500');
return;
}
//whatever you need to do here
//...
};
callback();
Також ви можете використовувати async.js .
Я думаю, що це краще, ніж $ .when тому, що ви можете об'єднати всі види асинхронного дзвінка, який не підтримує обіцянки поза коробкою, як тайм-аути, дзвінки SqlLite тощо, а не лише запити ajax.
На основі відповіді @BBonifield я написав функцію утиліти, щоб логіка семафору не поширювалася на всі виклики ajax.
untilAjax
це утиліта, яка викликає функцію зворотного виклику, коли всі ajaxCalls виконані.
ajaxObjs
це масив об'єктів налаштування ajax [http://api.jquery.com/jQuery.ajax/]
.
fn
є функцією зворотного дзвінка
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;
};
Приклад: doSomething
функція використовує 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.
});
}
Я настійно рекомендую використовувати $ .when (), якщо ви починаєте з нуля.
Незважаючи на те, що це питання має понад мільйон відповідей, я все одно не знайшов нічого корисного для моєї справи. Скажімо, вам доведеться мати справу з існуючою базою кодів, вже здійснюючи деякі дзвінки ajax і не хочуть вводити складність обіцянок та / або повторювати все.
Ми легко можемо скористатися JQuery .data
, .on
і .trigger
функції , які були частиною JQuery з тих пір назавжди.
Хороший матеріал про моє рішення:
очевидно, від чого саме залежить зворотний дзвінок
функцію triggerNowOrOnLoaded
не хвилює, чи дані вже завантажені чи ми їх ще чекаємо
дуже просто підключити його до наявного коду
$(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>
Я використовую перевірку розміру, коли завершено все завантаження 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>
Щоб розширити відповідь Алекса, я маю приклад зі змінними аргументами та обіцянками. Я хотів завантажити зображення через ajax і відображати їх на сторінці після завантаження.
Для цього я використав наступне:
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);
}
});
Оновлено для роботи для однієї або декількох URL-адрес: https://jsfiddle.net/euypj5w9/
Як і інші згадані відповіді, ви можете ajaxStop()
зачекати, поки всі запити на Ajax будуть виконані.
$(document).ajaxStop(function() {
// This function will be triggered every time any ajax request is requested and completed
});
Якщо ви хочете зробити це для конкретного ajax()
запиту, найкраще ви можете скористатися complete()
методом всередині певного запиту 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.
}
});
Але якщо вам потрібно зачекати лише декілька і певний запит на ajax? Використовуйте чудові javascript- обіцянки почекати, поки ці аякси, яких ви хочете чекати, будуть виконані. Я зробив короткий, простий і зрозумілий приклад, щоб показати вам, як обіцянки працюють з ajax.
Перегляньте наступний приклад . Я використовував setTimeout
для уточнення прикладу.
// 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>
Я знайшов простий спосіб, використовуючи його 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
Моє рішення полягає в наступному
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);
});
Працювали досить непогано. Я спробував багато різних способів зробити це, але виявив, що це найпростіший і багаторазовий використання. Сподіваюся, це допомагає
Подивіться на моє рішення:
1. Вставте цю функцію (та змінну) у свій файл 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.Будуйте масив зі своїми запитами, наприклад:
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.Створіть функцію зворотного виклику:
function Function_callback() {
alert('done');
}
4.Виклик функції runFunctionQueue з параметрами:
runFunctionQueue(f, 0, QuestionInsert_callback);
// first parameter: array with requests data
// second parameter: start from first request
// third parameter: the callback function
$.when
не працює для мене, callback(x)
а не return x
працює, як описано тут: https://stackoverflow.com/a/13455253/10357604
Спробуйте таким чином. зробіть цикл всередині функції сценарію java, щоб зачекати, поки виклик 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()
НЕ take a sleep
. У цьому випадку ви просто блокуєте всі вкладки, поки не done
стане істинним.
done
ніколи не буде правдою, поки цикл поки працює. Якщо цикл пока працює, цикл подій не може продовжуватися і тому ніколи не запускатиме зворотний виклик до успіху ajax.