Bagaimana cara membuat fungsi menunggu sampai semua permintaan jQuery Ajax dilakukan di dalam fungsi lain?
Singkatnya, saya harus menunggu semua permintaan Ajax selesai sebelum saya menjalankan selanjutnya. Tapi bagaimana caranya?
Bagaimana cara membuat fungsi menunggu sampai semua permintaan jQuery Ajax dilakukan di dalam fungsi lain?
Singkatnya, saya harus menunggu semua permintaan Ajax selesai sebelum saya menjalankan selanjutnya. Tapi bagaimana caranya?
Jawaban:
jQuery sekarang mendefinisikan fungsi when untuk tujuan ini.
Ia menerima sejumlah objek yang ditangguhkan sebagai argumen, dan menjalankan fungsi ketika semuanya diselesaikan.
Itu berarti, jika Anda ingin memulai (misalnya) empat permintaan ajax, kemudian melakukan tindakan ketika selesai, Anda dapat melakukan sesuatu seperti ini:
$.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,
...
});
}
Menurut pendapat saya, itu membuat sintaksis yang bersih dan jelas, dan menghindari melibatkan variabel global seperti ajaxStart dan ajaxStop, yang bisa memiliki efek samping yang tidak diinginkan saat halaman Anda berkembang.
Jika Anda tidak tahu sebelumnya berapa banyak argumen ajax yang harus Anda tunggu (yaitu Anda ingin menggunakan sejumlah variabel argumen), itu masih bisa dilakukan tetapi hanya sedikit lebih rumit. Lihat Menyerahkan array dari Ditangguhkan ke $ .when () (dan mungkin jQuery .when memecahkan masalah dengan jumlah variabel argumen ).
Jika Anda memerlukan kontrol lebih dalam atas mode kegagalan skrip ajax dll., Anda dapat menyimpan objek yang dikembalikan oleh .when()
- itu adalah objek jQuery Promise yang mencakup semua kueri ajax asli. Anda dapat memanggil .then()
atau .fail()
menambahkannya ke penangan sukses / kegagalan yang terperinci.
$.when
mengembalikan Promise
objek yang memiliki metode yang lebih bermanfaat, tidak hanya .done
. Misalnya, dengan .then(onSuccess, onFailure)
metode Anda dapat bereaksi ketika kedua permintaan berhasil atau setidaknya salah satu dari mereka gagal.
fail
kopernya. Tidak seperti done
, fail
kebakaran segera pada kegagalan pertama dan mengabaikan sisa tangguhan.
onFailure
fungsi dapat dilampirkan. Seperti yang saya tunjukkan dalam komentar pada pertanyaan OP: dia mungkin ingin menunjukkan lebih tepatnya apa yang dia maksud dengan "selesai". "Ryan Mohr" juga memiliki poin yang sangat bagus mengenai fakta yang fail
berperilaku berbeda done
, beberapa bacaan lebih lanjut harus dilakukanPromises
saya kira html5rocks.com/en/tutorials/es6/promises
Jika Anda ingin tahu kapan semua ajax
permintaan selesai di dokumen Anda, tidak peduli berapa banyak dari mereka ada, cukup gunakan $ .ajaxStop acara dengan cara ini:
$(document).ajaxStop(function () {
// 0 === $.active
});
Dalam hal ini, Anda tidak perlu menebak berapa banyak permintaan yang terjadi dalam aplikasi, yang mungkin selesai di masa depan, atau menggali logika fungsi kompleks atau menemukan fungsi mana yang melakukan
HTTP(S)
permintaan.
$.ajaxStop
di sini juga dapat terikat keHTML
simpul apa pun yang menurut Anda mungkin dimodifikasi oleh requst.
Pembaruan:
Jika Anda ingin tetap menggunakan ES
sintaks, maka Anda dapat menggunakan Promise.all untuk ajax
metode yang diketahui :
Promise.all([ajax1(), ajax2()]).then(() => {
// all requests finished successfully
}).catch(() => {
// all requests finished but one or more failed
})
Hal yang menarik di sini adalah bahwa ia berfungsi baik dengan Promises
dan $.ajax
meminta.
Ini adalah demonstrasi jsFiddle .
Pembaruan 2:
Namun versi yang lebih baru menggunakan async / menunggu sintaks :
try {
const results = await Promise.all([ajax1(), ajax2()])
// do other actions
} catch(ex) { }
Saya menemukan jawaban yang bagus oleh gnarf diri saya yang persis seperti yang saya cari :)
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);
Kemudian Anda dapat menambahkan permintaan ajax ke antrian seperti ini:
$.ajaxQueue({
url: 'page.php',
data: {id: 1},
type: 'POST',
success: function(data) {
$('#status').html(data);
}
});
Gunakan ajaxStop
acara tersebut.
Sebagai contoh, katakanlah Anda memiliki pesan memuat ... sambil mengambil 100 permintaan ajax dan Anda ingin menyembunyikan pesan itu sekali dimuat.
Dari dokumen jQuery :
$("#loading").ajaxStop(function() {
$(this).hide();
});
Perhatikan bahwa itu akan menunggu semua permintaan ajax dilakukan pada halaman itu.
CATATAN: Jawaban di atas menggunakan fungsionalitas yang tidak ada pada saat jawaban ini ditulis. Saya merekomendasikan penggunaan jQuery.when()
daripada pendekatan ini, tetapi saya meninggalkan jawaban untuk tujuan historis.
-
Anda mungkin bisa bertahan dengan semaphore penghitungan sederhana, meskipun bagaimana Anda menerapkannya akan tergantung pada kode Anda. Contoh sederhana akan menjadi seperti ...
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;
Jika Anda ingin ini beroperasi seperti {async: false} tetapi Anda tidak ingin mengunci browser, Anda bisa melakukan hal yang sama dengan antrian 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()
. Dengan begitu setidaknya Anda tidak menggandakan kode itu. Tidak hanya itu tetapi menyatakan function(){}
setiap waktu mengalokasikan memori setiap kali! Agak praktik yang buruk jika Anda bisa memanggil fungsi yang ditentukan secara statis.
javascript berbasis acara, jadi Anda tidak boleh menunggu , lebih baik mengatur kait / panggilan balik
Anda mungkin bisa menggunakan metode sukses / lengkap dari jquery.ajax
Atau Anda bisa menggunakan .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...
}
});
meskipun Anda harus memposting kode semu tentang bagaimana permintaan ajax Anda dipanggil lebih tepat ...
Solusi kecil adalah sesuatu seperti ini:
// 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();
});
Semoga bisa bermanfaat ...
jQuery memungkinkan Anda menentukan apakah Anda ingin permintaan ajax tidak sinkron atau tidak. Anda cukup membuat ajax permintaan sinkron dan kemudian sisa kode tidak akan dijalankan sampai mereka kembali.
Sebagai contoh:
jQuery.ajax({
async: false,
//code
});
Jika Anda membutuhkan sesuatu yang sederhana; sekali dan dilakukan panggilan balik
//multiple ajax calls above
var callback = function () {
if ($.active !== 0) {
setTimeout(callback, '500');
return;
}
//whatever you need to do here
//...
};
callback();
Anda juga dapat menggunakan async.js .
Saya pikir ini lebih baik daripada $ .Ketika karena Anda dapat menggabungkan semua jenis panggilan asinkron yang tidak mendukung janji di luar kotak seperti batas waktu, panggilan SqlLite dll, dan bukan hanya permintaan ajax.
Atas dasar jawaban @BBonifield, saya menulis fungsi utilitas sehingga logika semaphore tidak menyebar di semua panggilan ajax.
untilAjax
adalah fungsi utilitas yang memanggil fungsi panggilan balik ketika semua panggilan ajax selesai.
ajaxObjs
adalah array dari objek pengaturan ajax [http://api.jquery.com/jQuery.ajax/]
.
fn
adalah fungsi callback
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;
};
Contoh: doSomething
fungsi menggunakan 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.
});
}
Saya sangat merekomendasikan menggunakan $ .when () jika Anda memulai dari awal.
Meskipun pertanyaan ini memiliki lebih dari jutaan jawaban, saya masih tidak menemukan sesuatu yang berguna untuk kasus saya. Katakanlah Anda harus berurusan dengan basis kode yang ada, sudah melakukan beberapa panggilan ajax dan tidak ingin memperkenalkan kompleksitas janji dan / atau mengulang semuanya.
Kita dapat dengan mudah memanfaatkan jQuery .data
, .on
dan .trigger
fungsi yang telah menjadi bagian dari jQuery sejak selamanya.
Hal baik tentang solusi saya adalah:
sudah jelas apa yang sebenarnya bergantung pada callback
fungsi triggerNowOrOnLoaded
tidak peduli jika data telah dimuat atau kami masih menunggu untuk itu
sangat mudah untuk menghubungkannya ke kode yang sudah ada
$(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>
Saya menggunakan pemeriksaan ukuran ketika semua beban ajax selesai
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>
Untuk memperluas jawaban Alex, saya punya contoh dengan argumen dan janji variabel. Saya ingin memuat gambar melalui ajax dan menampilkannya di halaman setelah semuanya dimuat.
Untuk melakukan itu, saya menggunakan yang berikut:
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);
}
});
Diperbarui untuk bekerja baik untuk url tunggal atau ganda: https://jsfiddle.net/euypj5w9/
Seperti jawaban lain yang disebutkan, Anda dapat menggunakan ajaxStop()
untuk menunggu sampai semua permintaan ajax selesai.
$(document).ajaxStop(function() {
// This function will be triggered every time any ajax request is requested and completed
});
Jika Anda ingin melakukannya untuk ajax()
permintaan tertentu , yang terbaik yang dapat Anda lakukan adalah menggunakan complete()
metode di dalam permintaan ajax tertentu:
$.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.
}
});
Tapi, jika Anda hanya perlu menunggu beberapa permintaan ajax dan pasti dilakukan? Gunakan janji javascript yang luar biasa untuk menunggu sampai ajax yang ingin Anda tunggu selesai. Saya membuat contoh singkat, mudah dan mudah dibaca untuk menunjukkan kepada Anda bagaimana cara kerja janji dengan ajax.
Silakan lihat contoh berikut . Saya biasa setTimeout
mengklarifikasi contoh.
// 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>
Saya menemukan cara sederhana, menggunakan 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
Solusi saya adalah sebagai berikut
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);
});
Bekerja dengan cukup baik. Saya sudah mencoba banyak cara berbeda untuk melakukan ini, tetapi saya menemukan ini sebagai yang paling sederhana dan paling dapat digunakan kembali. Semoga ini bisa membantu
Lihatlah solusi saya:
1. Masukkan fungsi ini (dan variabel) ke file javascript Anda:
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. Buat array dengan permintaan Anda, seperti ini:
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.Create fungsi callback:
function Function_callback() {
alert('done');
}
4. Panggil fungsi runFunctionQueue dengan parameter:
runFunctionQueue(f, 0, QuestionInsert_callback);
// first parameter: array with requests data
// second parameter: start from first request
// third parameter: the callback function
$.when
tidak berfungsi untuk saya, callback(x)
alih-alih return x
berfungsi seperti yang dijelaskan di sini: https://stackoverflow.com/a/13455253/10357604
Coba dengan cara ini. buat loop di dalam fungsi skrip java untuk menunggu sampai panggilan ajax selesai.
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()
TIDAK take a sleep
. Dalam hal ini, Anda hanya memblokir semua tab hingga done
menjadi benar.
done
tidak akan pernah benar ketika loop sementara masih berjalan. Jika loop sementara sedang berjalan, loop peristiwa tidak dapat dilanjutkan dan oleh karena itu tidak akan pernah menjalankan panggilan balik ke keberhasilan ajax.