Как сделать функцию до тех пор, пока все запросы jQuery Ajax не будут выполнены внутри другой функции?
Короче говоря, мне нужно дождаться, когда все запросы Ajax будут выполнены до того, как я буду выполнять следующий. Но как?
Как сделать функцию до тех пор, пока все запросы jQuery Ajax не будут выполнены внутри другой функции?
Короче говоря, мне нужно дождаться, когда все запросы Ajax будут выполнены до того, как я буду выполнять следующий. Но как?
jQuery теперь определяет функцию when для этой цели.
Он принимает любое количество отложенных объектов в качестве аргументов и выполняет функцию, когда все они разрешаются.
Это означает, что если вы хотите инициировать (например) четыре запроса 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()
чтобы добавить подробные обработчики успеха/отказа.
Если вы хотите дождаться завершения всех запросов ajax в вашем документе, независимо от того, сколько из них существует, просто используйте событие $.ajaxStop
таким образом:
$(document).ajaxStop(function () {
// 0 === $.active
});
В этом случае нет необходимости угадывать, сколько запросов может быть в приложении, которое может завершиться в будущем. В некоторых случаях запросы ajax могут быть частью внутренней логики функции, которая может быть довольно сложной (например, вызов других функций), и в этом случае вы можете не дождаться, когда эта функция будет выполнена со всей логикой, а не только для ожидания ajax
для завершения.
$.ajaxStop
здесь также может быть привязан к любому узлу HTML
который, по вашему мнению, может быть изменен ajax
.
Опять же, цель этого обработчика - знать, когда нет активного ajax
чтобы не очистить или не перезагрузить что-то.
PS Если вы не против использования синтаксиса ES6, вы можете использовать Promise.all
для известных методов ajax
. Пример:
Promise.all([ajax1(), ajax2()]).then(() => {
// all requests finished successfully
}).catch(() => {
// all requests finished but one or more failed
})
Интересным моментом здесь является то, что он работает как с Promises
и с запросами $.ajax
. Вот jsFiddle, демонстрирующий последний.
Я нашел хороший ответ gnarf, который я точно искал:)
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);
}
});
ПРИМЕЧАНИЕ.. В приведенных выше ответах используются функциональные возможности, которых не было в то время, когда этот ответ был написан. Я рекомендую использовать 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();
});
});
Используйте событие ajaxStop
.
Например, скажем, у вас есть сообщение Загрузка... при извлечении 100 аякс-запросов и вы хотите скрыть это сообщение после загрузки.
Из jQuery doc:
$("#loading").ajaxStop(function() {
$(this).hide();
});
Обратите внимание, что он будет ожидать выполнения всех аякс-запросов на этой странице.
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
});
На основе ответа @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.
});
}
Если вам нужно что-то простое; один раз и закончил обратный вызов
//multiple ajax calls above
var callback = function () {
if ($.active !== 0) {
setTimeout(callback, '500');
return;
}
//whatever you need to do here
//...
};
callback();
Также вы можете использовать async.js.
Я думаю, что это лучше, чем $., потому что вы можете объединить все виды асинхронных вызовов, которые не поддерживают promises из коробки, такие как таймауты, вызовы SqlLite и т.д., а не просто запросы ajax.
Я настоятельно рекомендую использовать $. when(), если вы начинаете с нуля.
Несмотря на то, что этот вопрос содержит более миллиона ответов, я до сих пор не нашел ничего полезного для моего дела. Скажем, вам нужно иметь дело с существующей кодовой базой, уже делая некоторые вызовы ajax и не желая вводить сложность promises и/или переделывать все это.
Мы можем легко воспользоваться функциями 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? Используйте замечательные jaascript обещания, чтобы дождаться завершения этих ajax, которые вы хотите ждать. Я сделал короткий, простой и понятный пример, чтобы показать вам, как обещания работают с 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>
Посмотрите на мое решение:
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.Buil массив с вашими запросами, например:
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
Я встретил эту проблему и создал общий плагин jquery_counter, чтобы решить эту проблему: https://bitbucket.org/stxnext/jquery_counter/
Мое решение выглядит следующим образом
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);
});
Работала довольно хорошо. Я пробовал много разных способов сделать это, но я нашел, что это самый простой и многоразовый. Надеюсь, что это поможет.
Я нашел простой способ, используя 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
$.when
не работает для меня, callback(x)
вместо return x
работал, как описано здесь: fooobar.com/questions/208929/...
Решение, данное Алексом, отлично работает. Такая же концепция, но использование ее немного по-другому (когда количество вызовов неизвестно заранее)
http://garbageoverflow.blogspot.com/2014/02/wait-for-n-or-multiple-or-unknown.html
Самое элегантное решение, которое я нашел, которое лучше всего работает с несколькими асинхронными операциями, в отсутствие библиотеки, такой как jQuery или объект, подобный jQuery .when()
, заключается в том, чтобы каждый обработчик выполнения Promise приложил обработчик выполнения к одному из других Promises, как это (предполагая, что обещание1, обещание2 и обещание3 являются экземплярами Promise неизвестного состояния):
promise1.then(function(value)
{
promise2.then(function(value2)
{
promise3.then(function(value3)
{
doSomethingWithValues(value1, value2, value3);
}
}
});
Это работает, потому что выполненные/отклоненные обратные вызовы немедленно срабатывают, если они прикреплены после того, как обещание выполнено или отклонено. Поэтому, даже если promise3
выполняется в первую очередь, его обратный вызов выполнения не будет выполняться до тех пор, пока он не будет присоединен, что не произойдет до тех пор, пока promise1
и promise2
не будут выполнены. Недостатком этого подхода является то, что вложенные выражения функций могут быть немного запутанными. С небольшим количеством рефакторинга мы можем устранить необходимость вложенных функций;
var value1, value2;
function handlePromise1Fulfillment(value)
{
value1 = value;
promise2.then(handlePromise2Fulfillment);
}
function handlePromise2Fulfillment(value)
{
value2 = value;
promise3.then(handlePromise3Fulfillment);
}
function handlePromise3Fulfillment(value)
{
doSomethingWithValues(value1, value2, value);
}
promise1.then(handlePromise1Fulfillment);
С этой идиомой мы можем построить неограниченные конъюнкции Promises, не уменьшая удобочитаемость до ужасной степени. И с этой идиомой мы имеем основание для (простой, неполной) when()
функции нашей собственной:
function when()
{
var fulfillmentHandlers = [];
var values = [];
var promises = arguments;
return new Promise(function(resolve, reject)
{
fulfillmentHandlers.push(function(value)
{
values.push(value);
resolve(values.reverse());
});
var i;
// The i declaration in the for loop would be hoisted outside of the loop, but we might as well make this explicit
for (i = 0; i < promises.length - 1; i++)
{
fulfillmentHandlers[i + 1] = (function(iterator)
{
return function(value)
{
values.push(value);
promises[iterator].then(fulfillmentHandlers[iterator]);
}
})(i);
}
promises[i].then(fulfillmentHandlers[i]);
});
}
var promise1 = new Promise(function(resolve, reject)
{
setTimeout(function() { resolve('JavaScript '); }, 5000);
});
var promise2 = new Promise(function(resolve)
{
setTimeout(function() { resolve('is '); }, 4000);
});
var promise3 = new Promise(function(resolve)
{
setTimeout(function() { resolve('fun!'); }, 3000);
});
when(promise1, promise2, promise3).then(function(values)
{
console.log(values[0] + values[1] + values[2]);
});
Попробуйте этот путь. сделайте цикл внутри java script, чтобы ждать завершения вызова 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;
}