How to await the ajax request? - javascript

I am trying to write a JS code that will cancel the "btn_submit" buttons .onclick event if the given number already exists in the database. I use AJAX to query the DB for the given number and to determine if the should send the data to a .php site which will upload the question. To determine this I need the numOfRows variable's value, but because I set it in AJAX it will stay on 0. The validation() function will finish before my AJAX query finishes and this causes the problem that will always state that the given number does not exist in the DB (numOfRows will always stay on 0).
How can I await the AJAX query's finish before I compare the numOfRows to 0 in my validation() function's ending lines? If the number already exists in the DB, I need to return false to this line:
document.getElementById("btn_submit").onclick = validation;
Thank you!
var textAreaList;
var numOfRows = 0;
var finished = false;
document.getElementById("btn_submit").onclick = validation;
textAreaList = document.getElementsByClassName("text_input");
function validation() {
loadNumRows();
try {
document.getElementById('failure').hidden = true;
}
catch(e) {
console.log(e.message);
}
textAreaList = document.getElementsByClassName("text_input");
var failValidation = false;
for (var i = 0; i < textAreaList.length; i++) {
console.log(textAreaList[i]);
if (textAreaList[i].value == "") {
textAreaList[i].style.border = "2px solid #ff0000";
failValidation = true;
} else {
textAreaList[i].style.border = "2px solid #286C2B";
}
}
return !(failValidation || numOfRows != 0);
}
function loadNumRows(){
$.ajax({
url: 'php/SeeIfNumberExists?number=' + document.getElementById('number_inp').value,
type: "GET",
cache: false,
success: function (html) {
numOfRows = parseInt(html);
}
});
}

use of async/await with a transpilers like Babel to get it working in older browsers. You’ll also have to install this Babel preset and polyfill from npm:
npm i -D babel-preset-env babel-polyfill
Then
function getData(ajaxurl) {
return $.ajax({
url: ajaxurl,
type: 'GET',
});
};
async function test() {
try {
const res = await getData('https://api.icndb.com/jokes/random')
console.log(res)
} catch(err) {
console.log(err);
}
}
test();
or the .then callback is just another way to write the same logic.
getData(ajaxurl).then((res) => {
console.log(res)
});

Using async: false is an extremely bad idea, and defeats the whole purpose of using AJAX at the first place — AJAX is meant to be asynchronous. If you want to wait for a response from your script when you make the AJAX call, simply use deferred objects and promises:
var validation = function () {
var numberCheck = $.ajax({
url: 'php/SeeIfNumberExists?number=' + $('#number_inp').val(),
type: "GET"
});
// Listen to AJAX completion
numberCheck.done(function(html) {
var numOfRows = parseInt(html),
textAreaList = $('.text_input'),
finished = false;
// Rest of your code starts here
try {
document.getElementById('failure').hidden = true;
}
catch(e) {
console.log(e.message);
}
// ... and the rest
});
}
// Bind events using jQuery
$('#btn_submit').click(validation);
I see in your code that you are using a mixture of both native JS and jQuery — it helps if you stick to one :)

Never use async:false its dangerous, your app might misbehave.
You can use await only when your response returns a promise.
Unfortunately jQuery ajax doesn't return Promise when its completed.
But you can use promise in ajax request and return the promise when its done.
function asyncAjax(url){
return new Promise(function(resolve, reject) {
$.ajax({
url: url,
type: "GET",
dataType: "json",
beforeSend: function() {
},
success: function(data) {
resolve(data) // Resolve promise and when success
},
error: function(err) {
reject(err) // Reject the promise and go to catch()
}
});
});
}
We have converted ajax call into promise so now we can use await.
try{
const result = await asyncAjax('your url');
} catch(e){
console.log(e);
}

this works for me
async function doAjax() {
const result = await $.ajax({
url: "https://api.exchangerate-api.com/v4/latest/USD",
type: 'GET',
});
return result;
}
async function tt(){
var res = await doAjax()
var money = res.rates.INR
console.log(money)
}
tt()

(I acknowledge this is not the best way to go about things, but this is the quickest way to get your code working as is. Really though, you should rethink how you are pulling the numOfRows value so that it will work with truly asynchronous Ajax. All that being said...):
Start by setting async : false in the $.ajax call. The A in Ajax stands for asynchronous. That means, execution continues rather than waiting for it to return. You want to turn that off (i.e. make it synchronous). Actually, that should be the whole solution given the code you have there.
$.ajax({
url: 'php/SeeIfNumberExists?number=' + document.getElementById('number_inp').value,
type: "GET",
async: false,
cache: false,
success: function (html) {
numOfRows = parseInt(html);
}
});
One caveat from the docs for $.ajax:
Cross-domain requests and dataType: "jsonp" requests do not support synchronous operation. Note that synchronous requests may temporarily lock the browser, disabling any actions while the request is active. As of jQuery 1.8, the use of async: false with jqXHR ($.Deferred) is deprecated; you must use the success/error/complete callback options instead of the corresponding methods of the jqXHR object such as jqXHR.done() or the deprecated jqXHR.success().

Related

Do something after all ajax posts request are done [duplicate]

How do I make a function wait until all jQuery Ajax requests are done inside another function?
In short, I need to wait for all Ajax requests to be done before I execute the next. But how?
jQuery now defines a when function for this purpose.
It accepts any number of Deferred objects as arguments, and executes a function when all of them resolve.
That means, if you want to initiate (for example) four ajax requests, then perform an action when they are done, you could do something like this:
$.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,
...
});
}
In my opinion, it makes for a clean and clear syntax, and avoids involving any global variables such as ajaxStart and ajaxStop, which could have unwanted side effects as your page develops.
If you don't know in advance how many ajax arguments you need to wait for (i.e. you want to use a variable number of arguments), it can still be done but is just a little bit trickier. See Pass in an array of Deferreds to $.when() (and maybe jQuery .when troubleshooting with variable number of arguments).
If you need deeper control over the failure modes of the ajax scripts etc., you can save the object returned by .when() - it's a jQuery Promise object encompassing all of the original ajax queries. You can call .then() or .fail() on it to add detailed success/failure handlers.
If you want to know when all ajax requests are finished in your document, no matter how many of them exists, just use $.ajaxStop event this way:
$(document).ajaxStop(function () {
// 0 === $.active
});
In this case, neither you need to guess how many requests are happening in the application, that might finish in the future, nor dig into functions complex logic or find which functions are doing HTTP(S) requests.
$.ajaxStop here can also be bound to any HTML node that you
think might be modified by requst.
Update:
If you want to stick with ES syntax, then you can use Promise.all for known ajax methods:
Promise.all([ajax1(), ajax2()]).then(() => {
// all requests finished successfully
}).catch(() => {
// all requests finished but one or more failed
})
An interesting point here is that it works both with Promises and $.ajax requests.
Here is the jsFiddle demonstration.
Update 2:
Yet more recent version using async/await syntax:
try {
const results = await Promise.all([ajax1(), ajax2()])
// do other actions
} catch(ex) { }
I found a good answer by gnarf my self which is exactly what I was looking for :)
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);
Then you can add a ajax request to the queue like this:
$.ajaxQueue({
url: 'page.php',
data: {id: 1},
type: 'POST',
success: function(data) {
$('#status').html(data);
}
});
Use the ajaxStop event.
For example, let's say you have a loading ... message while fetching 100 ajax requests and you want to hide that message once loaded.
From the jQuery doc:
$("#loading").ajaxStop(function() {
$(this).hide();
});
Do note that it will wait for all ajax requests being done on that page.
NOTE: The above answers use functionality that didn't exist at the time that this answer was written. I recommend using jQuery.when() instead of these approaches, but I'm leaving the answer for historical purposes.
-
You could probably get by with a simple counting semaphore, although how you implement it would be dependent on your code. A simple example would be something like...
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;
If you wanted this to operate like {async: false} but you didn't want to lock the browser, you could accomplish the same thing with a jQuery queue.
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();
});
});
A little workaround is something like this:
// 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();
});
Hope can be useful...
javascript is event-based, so you should never wait, rather set hooks/callbacks
You can probably just use the success/complete methods of jquery.ajax
Or you could use .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...
}
});
though youy should post a pseudocode of how your(s) ajax request(s) is(are) called to be more precise...
jQuery allows you to specify if you want the ajax request to be asynchronous or not. You can simply make the ajax requests synchronous and then the rest of the code won't execute until they return.
For example:
jQuery.ajax({
async: false,
//code
});
As other answers mentioned you can use ajaxStop() to wait until all ajax request are completed.
$(document).ajaxStop(function() {
// This function will be triggered every time any ajax request is requested and completed
});
If you want do it for an specific ajax() request the best you can do is use complete() method inside the certain ajax request:
$.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.
}
});
But, If you need to wait only for a few and certain ajax request to be done? Use the wonderful javascript promises to wait until the these ajax you want to wait are done. I made a shortly, easy and readable example to show you how does promises works with ajax. Please take a look to the next example. I used setTimeout to clarify the example.
// 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>
If you need something simple; once and done callback
//multiple ajax calls above
var callback = function () {
if ($.active !== 0) {
setTimeout(callback, '500');
return;
}
//whatever you need to do here
//...
};
callback();
Also you could use async.js.
I think its better than $.when because you can merge all kinds of asynchronous call that does not support promises out of the box like timeouts, SqlLite calls etc. and not just ajax requests.
On the basis of #BBonifield answer, I wrote a utility function so that semaphore logic is not spread in all the ajax calls.
untilAjax is the utility function which invokes a callback function when all the ajaxCalls are completed.
ajaxObjs is a array of ajax setting objects [http://api.jquery.com/jQuery.ajax/].
fn is callback function
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;
};
Example: doSomething function uses 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.
});
}
I highly recommend using $.when() if you're starting from scratch.
Even though this question has over million answers, I still didn't find anything useful for my case. Let's say you have to deal with an existing codebase, already making some ajax calls and don't want to introduce the complexity of promises and/or redo the whole thing.
We can easily take advantage of jQuery .data, .on and .trigger functions which have been a part of jQuery since forever.
Codepen
The good stuff about my solution is:
it's obvious what the callback exactly depends on
the function triggerNowOrOnLoaded doesn't care if the data has been already loaded or we're still waiting for it
it's super easy to plug it into an existing code
$(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>
I'm using size check when all ajax load completed
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>
To expand upon Alex's answer, I have an example with variable arguments and promises. I wanted to load images via ajax and display them on the page after they all loaded.
To do that, I used the following:
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);
}
});
Updated to work for either single or multiple urls: https://jsfiddle.net/euypj5w9/
I found simple way, it using 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
My solution is as follows
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);
});
Worked quite nicely. I've tried a lot of different ways of doing this, but I found this to be the simplest and most reusable. Hope it helps
Look at my solution:
1.Insert this function (and variable) into your javascript file:
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 an array with your requests, like this:
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 callback function:
function Function_callback() {
alert('done');
}
4.Call the runFunctionQueue function with parameters:
runFunctionQueue(f, 0, QuestionInsert_callback);
// first parameter: array with requests data
// second parameter: start from first request
// third parameter: the callback function
$.when doesn't work for me, callback(x) instead of return x worked as described here: https://stackoverflow.com/a/13455253/10357604
The below solution worked for me using $when
$.when(master.GetStateByName(stateName)).done(function(response) {
if (response) {
}
});
GetStateByName: function(stateName) {
return $.ajax({
type: 'POST',
url: getStatesByName + '?stateName=' + stateName,
async: false,
});
}
This is working for me
It's very simple
return $.ajax({
type: 'POST',
url: urlBaseUrl
data: {someData:someData},
dataType: "json",
success: function(resultData) {
}
});
Try this way. make a loop inside java script function to wait until the ajax call finished.
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;
}

How can i excute a function after all ajax requests complete? [duplicate]

How do I make a function wait until all jQuery Ajax requests are done inside another function?
In short, I need to wait for all Ajax requests to be done before I execute the next. But how?
jQuery now defines a when function for this purpose.
It accepts any number of Deferred objects as arguments, and executes a function when all of them resolve.
That means, if you want to initiate (for example) four ajax requests, then perform an action when they are done, you could do something like this:
$.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,
...
});
}
In my opinion, it makes for a clean and clear syntax, and avoids involving any global variables such as ajaxStart and ajaxStop, which could have unwanted side effects as your page develops.
If you don't know in advance how many ajax arguments you need to wait for (i.e. you want to use a variable number of arguments), it can still be done but is just a little bit trickier. See Pass in an array of Deferreds to $.when() (and maybe jQuery .when troubleshooting with variable number of arguments).
If you need deeper control over the failure modes of the ajax scripts etc., you can save the object returned by .when() - it's a jQuery Promise object encompassing all of the original ajax queries. You can call .then() or .fail() on it to add detailed success/failure handlers.
If you want to know when all ajax requests are finished in your document, no matter how many of them exists, just use $.ajaxStop event this way:
$(document).ajaxStop(function () {
// 0 === $.active
});
In this case, neither you need to guess how many requests are happening in the application, that might finish in the future, nor dig into functions complex logic or find which functions are doing HTTP(S) requests.
$.ajaxStop here can also be bound to any HTML node that you
think might be modified by requst.
Update:
If you want to stick with ES syntax, then you can use Promise.all for known ajax methods:
Promise.all([ajax1(), ajax2()]).then(() => {
// all requests finished successfully
}).catch(() => {
// all requests finished but one or more failed
})
An interesting point here is that it works both with Promises and $.ajax requests.
Here is the jsFiddle demonstration.
Update 2:
Yet more recent version using async/await syntax:
try {
const results = await Promise.all([ajax1(), ajax2()])
// do other actions
} catch(ex) { }
I found a good answer by gnarf my self which is exactly what I was looking for :)
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);
Then you can add a ajax request to the queue like this:
$.ajaxQueue({
url: 'page.php',
data: {id: 1},
type: 'POST',
success: function(data) {
$('#status').html(data);
}
});
Use the ajaxStop event.
For example, let's say you have a loading ... message while fetching 100 ajax requests and you want to hide that message once loaded.
From the jQuery doc:
$("#loading").ajaxStop(function() {
$(this).hide();
});
Do note that it will wait for all ajax requests being done on that page.
NOTE: The above answers use functionality that didn't exist at the time that this answer was written. I recommend using jQuery.when() instead of these approaches, but I'm leaving the answer for historical purposes.
-
You could probably get by with a simple counting semaphore, although how you implement it would be dependent on your code. A simple example would be something like...
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;
If you wanted this to operate like {async: false} but you didn't want to lock the browser, you could accomplish the same thing with a jQuery queue.
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();
});
});
A little workaround is something like this:
// 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();
});
Hope can be useful...
javascript is event-based, so you should never wait, rather set hooks/callbacks
You can probably just use the success/complete methods of jquery.ajax
Or you could use .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...
}
});
though youy should post a pseudocode of how your(s) ajax request(s) is(are) called to be more precise...
jQuery allows you to specify if you want the ajax request to be asynchronous or not. You can simply make the ajax requests synchronous and then the rest of the code won't execute until they return.
For example:
jQuery.ajax({
async: false,
//code
});
As other answers mentioned you can use ajaxStop() to wait until all ajax request are completed.
$(document).ajaxStop(function() {
// This function will be triggered every time any ajax request is requested and completed
});
If you want do it for an specific ajax() request the best you can do is use complete() method inside the certain ajax request:
$.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.
}
});
But, If you need to wait only for a few and certain ajax request to be done? Use the wonderful javascript promises to wait until the these ajax you want to wait are done. I made a shortly, easy and readable example to show you how does promises works with ajax. Please take a look to the next example. I used setTimeout to clarify the example.
// 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>
If you need something simple; once and done callback
//multiple ajax calls above
var callback = function () {
if ($.active !== 0) {
setTimeout(callback, '500');
return;
}
//whatever you need to do here
//...
};
callback();
Also you could use async.js.
I think its better than $.when because you can merge all kinds of asynchronous call that does not support promises out of the box like timeouts, SqlLite calls etc. and not just ajax requests.
On the basis of #BBonifield answer, I wrote a utility function so that semaphore logic is not spread in all the ajax calls.
untilAjax is the utility function which invokes a callback function when all the ajaxCalls are completed.
ajaxObjs is a array of ajax setting objects [http://api.jquery.com/jQuery.ajax/].
fn is callback function
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;
};
Example: doSomething function uses 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.
});
}
I highly recommend using $.when() if you're starting from scratch.
Even though this question has over million answers, I still didn't find anything useful for my case. Let's say you have to deal with an existing codebase, already making some ajax calls and don't want to introduce the complexity of promises and/or redo the whole thing.
We can easily take advantage of jQuery .data, .on and .trigger functions which have been a part of jQuery since forever.
Codepen
The good stuff about my solution is:
it's obvious what the callback exactly depends on
the function triggerNowOrOnLoaded doesn't care if the data has been already loaded or we're still waiting for it
it's super easy to plug it into an existing code
$(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>
I'm using size check when all ajax load completed
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>
To expand upon Alex's answer, I have an example with variable arguments and promises. I wanted to load images via ajax and display them on the page after they all loaded.
To do that, I used the following:
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);
}
});
Updated to work for either single or multiple urls: https://jsfiddle.net/euypj5w9/
I found simple way, it using 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
My solution is as follows
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);
});
Worked quite nicely. I've tried a lot of different ways of doing this, but I found this to be the simplest and most reusable. Hope it helps
Look at my solution:
1.Insert this function (and variable) into your javascript file:
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 an array with your requests, like this:
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 callback function:
function Function_callback() {
alert('done');
}
4.Call the runFunctionQueue function with parameters:
runFunctionQueue(f, 0, QuestionInsert_callback);
// first parameter: array with requests data
// second parameter: start from first request
// third parameter: the callback function
$.when doesn't work for me, callback(x) instead of return x worked as described here: https://stackoverflow.com/a/13455253/10357604
The below solution worked for me using $when
$.when(master.GetStateByName(stateName)).done(function(response) {
if (response) {
}
});
GetStateByName: function(stateName) {
return $.ajax({
type: 'POST',
url: getStatesByName + '?stateName=' + stateName,
async: false,
});
}
This is working for me
It's very simple
return $.ajax({
type: 'POST',
url: urlBaseUrl
data: {someData:someData},
dataType: "json",
success: function(resultData) {
}
});
Try this way. make a loop inside java script function to wait until the ajax call finished.
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;
}

waiting for ajax to complete before next ajax call

I am trying to wait for an ajax call to finish saving a model before saving the next model in the list. I was googling around and saw some stuff about deferred objects which are new to me, and another answer that had a recursive function do it. I tried the recursive method because it seemed to make a little more sense than with deferred objects and using $.when.apply($, arrayOfAjaxCalls).then(). So that code (the recursive one, looks like:
saveModel(index, numRequests) {
var self = this;
if (index < numRequests) {
var sample = self.samplesToSave[index];
return $.ajax({
url: model.url,
contentType: "application/json",
type: "POST",
data: JSON.stringify(model),
crossDomain: $.support.cors,
xhrFields: {
withCredentials: $.support.cors,
},
success: function(data) {
console.log("JUST SAVED");
console.log(data);
},
error: function(xhr: any) {
console.log(xhr);
},
}).then(() => {
self.saveModel(index + 1, numRequests);
});
}
}
I call this like:
saveModel(0, _.size(myCollection)
It doesn't actually wait for the ajax call to finish in its current state before calling the next saveModel. It basically just synchronously calls saveModel for each item in the collection in order. Any thoughts on what I'm missing? If there's a better solution with $.Deferred, I'm ok with that as well. Thanks.
Edit: Sorry it meant to say saveModel in the last line of the saveModel function. Was trying to get rid of parts that were domain specific. And I'm using typescript, not coffeescript
New attempt:
saveSampleNew() {
var d = $.Deferred();
d.resolve();
var p = d.promise();
var self = this;
self.samplesToSave.forEach(sample => p = p.then(() => self.makeSaveRequest(sample)));
return p;
}
makeSaveRequest(sample) {
var self = this;
return $.ajax({
url: "samples",
contentType: "application/json",
type: "POST",
data: JSON.stringify(sample),
crossDomain: $.support.cors,
xhrFields: {
withCredentials: $.support.cors,
},
success: function(data) {
console.log("SAVED12");
console.log(data);
},
});
}
Because this code depends on other async calls from completing, I call this new attempt like this:
this.saveContainers(project).then(() => {
}).done(() => {
self.saveSampleNew();
});
No, it should work this way. If you think it doesn't wait, please provide more information on how you call it and how experience that it does synchronously recurse.
There is one catch however with the recursive call:
.then(function() {
self.saveModel(index + 1, numRequests);
})
The promise that is returned by then, and subsequently by your saveModel method, does resolve directly with the first ajax call, it does not wait for the recursive chain. The other ajax calls are still happening (sequentially, as expected), but are not being tracked by the resulting promise.
To get that, and properly chain the promises so that it resolves with the result of the last ("innermost") promise, you will need to return the promise from the callback to then:
.then(function() {
return self.saveModel(index + 1, numRequests);
})
I'd probably use a for loop rather than a recursive call here, generally - I find those easier to read in this context.
saveModel() {
var d = $.Deferred(); d.resolve();
var p = d.promise(); // to start the chain
this.samplesToSave.forEach(sample => p = p.then(() => makeSaveRequest(sample));
return p;
}
makeSaveRequest(sample) {
return $.ajax({...}); // make request using `sample` as data
}

Any way to do a synchronous PageMethods call?

I'm trying to do this:
function DelBatch()
{var userInfo = get_cookie("UserInfo");
PageMethods.DeleteBatchJSWM(userInfo, function(result)
{window.location = "BatchOperations.aspx";});
}
But it still runs asynchronously. I need the browser to actually wait until my code-behind is finished executing, then it can be refreshed
There's a listbox loaded with values that were just deleted from the database, they shouldn't be visible. Problem I have is the window location refreshes before the code-behind is executed, and nothing seems like it was deleted to the user.
Call it using jQuery ajax instead? It features an option (async) where you can select sync/async mode: http://api.jquery.com/jQuery.ajax/
This excellent article tells you how best to call PageMethods from jQuery: http://encosia.com/using-jquery-to-directly-call-aspnet-ajax-page-methods/
Essentially, all you will need to do is this:
$.ajax({
type: "POST",
async: false,
url: "yourpage.aspx/DeleteBatchJSWM",
data: "{ put json representation of userInfo here }",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(msg) {
window.location = "BatchOperations.aspx";
}
});
Look at Crockford's JSON stringify for a json formatting solution.
If you want to avoid using jQuery, a work around would be to use another PageMethod in which you check the status of the operation using the javascript setInterval function. It is a little messy, but it does the job if you want zero jQuery and it mimics the synchronicity you seek. I use it for large operations in which I want to update a progress bar to the client or something. Here would be an example of how you would do this given what code you posted:
function DelBatch()
{
var userInfo = get_cookie("UserInfo");
PageMethods.DeleteBatchJSWM(userInfo, function(result) {window.location = "BatchOperations.aspx";});
var status;
//Check to see if it has completed every second
var myInterval = setInterval(function ()
{
PageMethods.CheckDeleteBatchStatus(OnSuccess);
if (status == "Finished")
{
clearInterval(myInterval);
//Finished Deleting. Call your window refresh here
WindowRefresh();
}
}, 1000);
function OnSuccess(result)
{
status = result;
}
}
Code Behind:
[WebMethod]
public static string CheckDeleteBatchStatus()
{
string status = GetDeleteBatchStatus(); //some function to get the status of your operation
return status;
}
I came across this site:
http://abhijit-j-shetty.blogspot.com/2011/04/aspnet-ajax-calling-pagemethods.html
that had a great method for handling Synchronous PageMethod calls.
The javascript code is as follows:
// Make sure page methods operate synchronously
XMLHttpRequest.prototype.original_open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
async = false;
var eventArgs = Array.prototype.slice.call(arguments);
var q = 0;
return this.original_open.apply(this, eventArgs);
}
// Make a generic WebMethod caller:
function WebMethodCall(FunctionName, callingobj) {
var OnSuccess = function (result, userContext, methodName) {
callingobj.push(result);
}
var OnFailure = function (error, userContext, methodName) {
callingobj.push(error.get_message());
}
PageMethods[FunctionName](OnSuccess, OnFailure);
}
// OK, this is kludgy, but here goes. In order to have a synchronous PageMethod call
// we need an object that persists in the namespace to stuff the result value into (like an array)
// Essentially I'm emulating a ByRef call.
// ThisResult is an empty list. The WebMethodCall function sticks a value into that list.
// The code that makes the PageMethods get called synchronously is in Common.js
// Use the functions
var ThisResult = []; // This must be of a type which persists in the namespace
WebMethodCall('HelloWorld', ThisResult);
return ThisResult[0];
Using jQuery was first recommended back in 2009.
Another (extremely verbose) option is implementing a synchronous WebRequestExecutor as shown here (2007-07-04), and perfected here (2007-10-30). The gist of the technique is to copy the ASP.NET AJAX Sys.Net.XMLHttpExecutor as a new class named Sys.Net.XMLHttpSyncExecutor and change the call to xmlHttpRequest.open to pass false as the last parameter to force synchronous operation.
The synchronous executor can be plugged into all requests using WebRequestManager like this:
Sys.Net.WebRequestManager.set_defaultExecutorType('Sys.Net.XMLHttpSyncExecutor');
or you may want to switch it up per-request just before it is invoked:
Sys.Net.WebRequestManager.add_invokingRequest(function(sender, args) {
if (iFeelLikeRunningThisRequestSynchronously) {
args.get_webRequest().set_executor(new Sys.Net.XMLHttpSyncExecutor());
}});
This discussion is the source for most of these links and a few more.
I wrote this, that lets you call a PageMethod synchronously. It also will just return the result of the method, and throw an error that can be caught in a try/catch block, so you don't need to worry about supplying onSuccess and onError functions.
function synchronusPageMethod(method) {
XMLHttpRequest.prototype.original_open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
async = false;
var eventArgs = Array.prototype.slice.call(arguments);
return this.original_open.apply(this, eventArgs);
};
var result;
var error;
var args = Array.prototype.slice.call(arguments).slice(1);
args.push(function (res) {
result = res;
});
args.push(function (err) {
error = err;
});
method.apply(null, args);
XMLHttpRequest.prototype.open = XMLHttpRequest.prototype.original_open;
if (error !== undefined) {
throw error;
} else {
return result;
}
}
Use it like this:
try {
var result = synchronusPageMethod(PageMethods.myMethod, argument0, argument1);
console.log(result);
} catch(error) {
console.log(error);
}

How can I get jQuery to perform a synchronous, rather than asynchronous, Ajax request?

I have a JavaScript widget which provides standard extension points. One of them is the beforecreate function. It should return false to prevent an item from being created.
I've added an Ajax call into this function using jQuery:
beforecreate: function (node, targetNode, type, to) {
jQuery.get('http://example.com/catalog/create/' + targetNode.id + '?name=' + encode(to.inp[0].value),
function (result) {
if (result.isOk == false)
alert(result.message);
});
}
But I want to prevent my widget from creating the item, so I should return false in the mother-function, not in the callback. Is there a way to perform a synchronous AJAX request using jQuery or any other in-browser API?
From the jQuery documentation: you specify the asynchronous option to be false to get a synchronous Ajax request. Then your callback can set some data before your mother function proceeds.
Here's what your code would look like if changed as suggested:
beforecreate: function (node, targetNode, type, to) {
jQuery.ajax({
url: 'http://example.com/catalog/create/' + targetNode.id + '?name=' + encode(to.inp[0].value),
success: function (result) {
if (result.isOk == false) alert(result.message);
},
async: false
});
}
You can put the jQuery's Ajax setup in synchronous mode by calling
jQuery.ajaxSetup({async:false});
And then perform your Ajax calls using jQuery.get( ... );
Then just turning it on again once
jQuery.ajaxSetup({async:true});
I guess it works out the same thing as suggested by #Adam, but it might be helpful to someone that does want to reconfigure their jQuery.get() or jQuery.post() to the more elaborate jQuery.ajax() syntax.
Excellent solution! I noticed when I tried to implement it that if I returned a value in the success clause, it came back as undefined. I had to store it in a variable and return that variable. This is the method I came up with:
function getWhatever() {
// strUrl is whatever URL you need to call
var strUrl = "", strReturn = "";
jQuery.ajax({
url: strUrl,
success: function(html) {
strReturn = html;
},
async:false
});
return strReturn;
}
All of these answers miss the point that doing an Ajax call with async:false will cause the browser to hang until the Ajax request completes. Using a flow control library will solve this problem without hanging up the browser. Here is an example with Frame.js:
beforecreate: function(node,targetNode,type,to) {
Frame(function(next)){
jQuery.get('http://example.com/catalog/create/', next);
});
Frame(function(next, response)){
alert(response);
next();
});
Frame.init();
}
function getURL(url){
return $.ajax({
type: "GET",
url: url,
cache: false,
async: false
}).responseText;
}
//example use
var msg=getURL("message.php");
alert(msg);
Keep in mind that if you're doing a cross-domain Ajax call (by using JSONP) - you can't do it synchronously, the async flag will be ignored by jQuery.
$.ajax({
url: "testserver.php",
dataType: 'jsonp', // jsonp
async: false //IGNORED!!
});
For JSONP-calls you could use:
Ajax-call to your own domain - and do the cross-domain call server-side
Change your code to work asynchronously
Use a "function sequencer" library like Frame.js (this answer)
Block the UI instead of blocking the execution (this answer) (my favourite way)
Note: You shouldn't use async: false due to this warning messages:
Starting with Gecko 30.0 (Firefox 30.0 / Thunderbird 30.0 / SeaMonkey 2.27), synchronous requests on the main thread have been deprecated due to the negative effects to the user experience.
Chrome even warns about this in the console:
Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/.
This could break your page if you are doing something like this since it could stop working any day.
If you want to do it a way that still feels like if it's synchronous but still don't block then you should use async/await and probably also some ajax that is based on promises like the new Fetch API
async function foo() {
var res = await fetch(url)
console.log(res.ok)
var json = await res.json()
console.log(json)
}
Edit
chrome is working on Disallowing sync XHR in page dismissal when the page is being navigated away or closed by the user. This involves beforeunload, unload, pagehide and visibilitychange.
if this is your use case then you might want to have a look at navigator.sendBeacon instead
It is also possible for the page to disable sync req with either http headers or iframe's allow attribute
I used the answer given by Carcione and modified it to use JSON.
function getUrlJsonSync(url){
var jqxhr = $.ajax({
type: "GET",
url: url,
dataType: 'json',
cache: false,
async: false
});
// 'async' has to be 'false' for this to work
var response = {valid: jqxhr.statusText, data: jqxhr.responseJSON};
return response;
}
function testGetUrlJsonSync()
{
var reply = getUrlJsonSync("myurl");
if (reply.valid == 'OK')
{
console.dir(reply.data);
}
else
{
alert('not valid');
}
}
I added the dataType of 'JSON' and changed the .responseText to responseJSON.
I also retrieved the status using the statusText property of the returned object. Note, that this is the status of the Ajax response, not whether the JSON is valid.
The back-end has to return the response in correct (well-formed) JSON, otherwise the returned object will be undefined.
There are two aspects to consider when answering the original question. One is telling Ajax to perform synchronously (by setting async: false) and the other is returning the response via the calling function's return statement, rather than into a callback function.
I also tried it with POST and it worked.
I changed the GET to POST and added data: postdata
function postUrlJsonSync(url, postdata){
var jqxhr = $.ajax({
type: "POST",
url: url,
data: postdata,
dataType: 'json',
cache: false,
async: false
});
// 'async' has to be 'false' for this to work
var response = {valid: jqxhr.statusText, data: jqxhr.responseJSON};
return response;
}
Note that the above code only works in the case where async is false. If you were to set async: true the returned object jqxhr would not be valid at the time the AJAX call returns, only later when the asynchronous call has finished, but that is much too late to set the response variable.
With async: false you get yourself a blocked browser.
For a non blocking synchronous solution you can use the following:
ES6/ECMAScript2015
With ES6 you can use a generator & the co library:
beforecreate: function (node, targetNode, type, to) {
co(function*(){
let result = yield jQuery.get('http://example.com/catalog/create/' + targetNode.id + '?name=' + encode(to.inp[0].value));
//Just use the result here
});
}
ES7
With ES7 you can just use asyc await:
beforecreate: function (node, targetNode, type, to) {
(async function(){
let result = await jQuery.get('http://example.com/catalog/create/' + targetNode.id + '?name=' + encode(to.inp[0].value));
//Just use the result here
})();
}
This is example:
$.ajax({
url: "test.html",
async: false
}).done(function(data) {
// Todo something..
}).fail(function(xhr) {
// Todo something..
});
Firstly we should understand when we use $.ajax and when we use $.get/$.post
When we require low level control over the ajax request such as request header settings, caching settings, synchronous settings etc.then we should go for $.ajax.
$.get/$.post: When we do not require low level control over the ajax request.Only simple get/post the data to the server.It is shorthand of
$.ajax({
url: url,
data: data,
success: success,
dataType: dataType
});
and hence we can not use other features(sync,cache etc.) with $.get/$.post.
Hence for low level control(sync,cache,etc.) over ajax request,we should go for $.ajax
$.ajax({
type: 'GET',
url: url,
data: data,
success: success,
dataType: dataType,
async:false
});
this is my simple implementation for ASYNC requests with jQuery. I hope this help anyone.
var queueUrlsForRemove = [
'http://dev-myurl.com/image/1',
'http://dev-myurl.com/image/2',
'http://dev-myurl.com/image/3',
];
var queueImagesDelete = function(){
deleteImage( queueUrlsForRemove.splice(0,1), function(){
if (queueUrlsForRemove.length > 0) {
queueImagesDelete();
}
});
}
var deleteImage = function(url, callback) {
$.ajax({
url: url,
method: 'DELETE'
}).done(function(response){
typeof(callback) == 'function' ? callback(response) : null;
});
}
queueImagesDelete();
Because XMLHttpReponse synchronous operation is deprecated I came up with the following solution that wraps XMLHttpRequest. This allows ordered AJAX queries while still being asycnronous in nature, which is very useful for single use CSRF tokens.
It is also transparent so libraries such as jQuery will operate seamlessly.
/* wrap XMLHttpRequest for synchronous operation */
var XHRQueue = [];
var _XMLHttpRequest = XMLHttpRequest;
XMLHttpRequest = function()
{
var xhr = new _XMLHttpRequest();
var _send = xhr.send;
xhr.send = function()
{
/* queue the request, and if it's the first, process it */
XHRQueue.push([this, arguments]);
if (XHRQueue.length == 1)
this.processQueue();
};
xhr.processQueue = function()
{
var call = XHRQueue[0];
var xhr = call[0];
var args = call[1];
/* you could also set a CSRF token header here */
/* send the request */
_send.apply(xhr, args);
};
xhr.addEventListener('load', function(e)
{
/* you could also retrieve a CSRF token header here */
/* remove the completed request and if there is more, trigger the next */
XHRQueue.shift();
if (XHRQueue.length)
this.processQueue();
});
return xhr;
};
Since the original question was about jQuery.get, it is worth mentioning here that (as mentioned here) one could use async: false in a $.get() but ideally avoid it since asynchronous XMLHTTPRequest is deprecated (and the browser may give a warning):
$.get({
url: url,// mandatory
data: data,
success: success,
dataType: dataType,
async:false // to make it synchronous
});

Categories