I usually like to organize my code so that one function fires a bunch of other
functions, like this:
/**
* GET MESSAGES:
*/
$(function() {
$.ajax({
url: '/messages',
method: 'GET',
dataType: 'json',
success: function(messages) {
if (messages.length > 0) {
keyedMessages = keyFork(messages);
reversedMessages = reverse(keyedMessages);
crushedMessages = crush(reversedMessages);
getFriendships(messages, crushedMessages);
}
mail.template.airmail();
}
});
});
However, if I need to do a second Ajax request inside one of the nested
functions I can't return the data because of the scope of the Ajax request
and it makes my code inconsistent and hard to follow, sort of broken up all over the place. For example, if one of the functions
invoked above fires a second Ajax request for friendships anything I write
after that will be broken from the communication chain due to the request and it seems impossible to return anything:
/**
* GET FRIENDSHIPS:
*/
function getFriendships(messages, crushedMessages) {
$.ajax({
url: 'friendships',
method: 'get',
dataType: 'json',
success: function(friendships) {
addKey(crushedMessages, friendships);
filteredCrushedMessages = filterUnconfirmedSender(crushedMessages);
filteredCrushedMessages.forEach(function(filteredCrushedMessage) {
mail.sidebar.builder.messengers(filteredCrushedMessage);
});
mail.snailMail.onload();
}
});
}
If I try to return the data it doesn't work. Consequently I'll have to
continue invoking functions inside the nested request, every time I need to make another nested ajax request it breaks the chain. This makes my
code very hard to read. Are there any solutions to this problem or is
code that uses Ajax requests just hard to read?
You could store the data on a DOM element, then use jQuery Custom Events to get it done.
There's even support for passing arguments to your event handler:
https://learn.jquery.com/events/introduction-to-custom-events/#naming-custom-events
If I try to return the data it doesn't work.
Not appear jQuery promise returned from either function at Question ?
Try utilizing return statement , $.when.apply(this, arrayOfPromises) to return array of jQuery promise object from getFriendships
function getFriendships(messages, crushedMessages) {
return $.ajax({
url: 'friendships',
method: 'get',
dataType: 'json',
success: function(friendships) {
addKey(crushedMessages, friendships);
filteredCrushedMessages = filterUnconfirmedSender(crushedMessages);
mail.snailMail.onload();
return $.when.apply($
, filteredCrushedMessages.map(function(filteredCrushedMessage) {
return mail.sidebar.builder.messengers(filteredCrushedMessage);
})
);
}
});
}
// e.g.,
getFriendships(messages, crushedMessages)
.then(function success() {
console.log(arguments)
}, function err(jqxhr, textStatus, errorThrown) {
console.log(errorThrown)
})
I have a for loop in java script :
for(i=1;i<=10;i++){
$.ajax({
type: "GET",
url: "insert_names.php",
success: function (data) {
// some codes
}
});
}
this script send all of ajax request Simultaneously !
But It makes problem for me ...
how can i can prevent continue for loop when ajax is working ?
If you need the requests to be executed in serial instead of parallel, you can do that with a little re-structuring. Don't make the AJAX requests synchronous, just use the response of each request to invoke the next one.
Consider a structure like this:
function sendRequest() {
$.ajax({
type: "GET",
url: "insert_names.php",
success: function (data) {
// some codes
}
});
}
Now the request is wrapped in a function that you can invoke. So invoke it in response to the previous request:
function sendRequest() {
$.ajax({
type: "GET",
url: "insert_names.php",
success: function (data) {
// some codes
sendRequest();
}
});
}
It's starting to look like a standard recursive pattern, so all you need now is a terminating condition. Similar to the original loop, just use an incrementing value:
var i = 1;
function sendRequest() {
$.ajax({
type: "GET",
url: "insert_names.php",
success: function (data) {
// some codes
i++;
if (i <= 10) {
sendRequest();
}
}
});
}
Then just invoke it once to start it:
sendRequest();
Now the request should be invoked 10 times as expected (unless my math is off by one, but that should be easy to correct), each request happening in response to the previous request.
With asynchronous programming, don't try to make it synchronous. Instead, perform actions in response to the callbacks.
I want to do something like this:
$.ajax({
url: SOMEWHERE,
...
success: function(data){
// do sth...
var new_url = data.url;
$.ajax({
url: new_url,
success: function(data){
var another_url = data.url;
// ajax call rely on the result of previous one
$.ajax({
// do sth
})
}
})
},
fail: function(){
// do sth...
// ajax call too
$.ajax({
// config
})
}
})
the code looks awful for me.
I wonder how to make it looks pretty. Some best practice?
I would consider breaking it up, maybe something like this.
function initialSuccessHandler(data) {
$.ajax({url:data.url, success:secondarySuccessHandler});
}
function secondarySuccessHandler(data) {
//do stuff
}
function initialFailHandler() {
$.ajax({...});
}
$.ajax({url:"whatever.com", success:initialSuccessHandler, fail: initialFailHandler});
There's not a whole lot you can probably do about it other than if the success function are similar (just need different URL's to new AJAX calls for example) you might be able to define a common function to call recursively, like this:
function do_something(data) {
// some logic
$.ajax({
url: data.url,
success: do_something(data);
fail: function (){
// handle failure
}
});
}
Use $.post instead of $.ajax that's lot easier and clean
$.post('yourAjaxUrl.com/index.html',(newVal:'val'), function(data) {
$.post('yourSecondAjaxUrl.com/index.html',{newVal1:data}, function(data) {
//do something
});
});
Or if you want to use GET request use like this.
$.get('yourAjaxUrl.com/index.html',(newVal:'val'), function(data) {
$.get('yourSecondAjaxUrl.com/index.html',{newVal1:data}, function(data) {
//do something
});
});
Other answers are mostly fine too as using functions in a lot of case will definitely help your code. The problem of your function is that it's doing to much things all in once. Decreasing the complexity of the function will help a LOT (separating different action in different functions).
There's some good training videos of Bocoup here which can help you decrease a function complexity: http://training.bocoup.com/screencasts/
Although, a basic answer to the callback inferno:
You could use jquery Deffered who do a good job in certain case by preventing the "indentation pyramid of doom". (But won't decrease the complexity of your function)
$.ajax({
url: SOMEWHERE
})
.pipe(function() {
// First success callback
return $.ajax({
url: new_url
});
}, function() {
// First error callback
$.ajax({
// config
});
// we ain't returning anything so this end here.
})
.done(function( data ) {
// Second success callback
var another_url = data.url;
// ajax call rely on the result of previous one
$.ajax({
// do sth
})
});
Deferred can fit in a whole lot more of context, and learning about them is really useful. That's only the basic idea behind them.
Hope this help!
I am trying to write simple function that checks to see if a designer name exists in the database. I am using jQuery's ajax function to try to do this:
function checkDesignerName(name)
{
var designer_name = $('input[name="name"]').val();
var designer_exists = false;
var temp = $.ajax( { type: "GET",
url: "/api/check_brand_exists/",
data : {name : designer_name },
success: function(data) {
designer_exists = $.parseJSON(data);
return designer_exists;
}}).statusText;
return designer_exists;
}
I have read about javascript scoping, and but still can't seem to find my bug, which is checkDesignerName always returns false. Do I need to use a closure for this function to work correctly?
Thanks
It's the nature of AJAX which is asynchronous that you seem to have troubles with understanding.
At this stage:
return designer_exists;
your AJAX call hasn't yet finished. It's only inside the success callback, which happens much later, that you can use the results. You cannot have a function which returns some result and this result depends on an AJAX call. You can only exploit the results of an AJAX call iniside the success callback:
function checkDesignerName(name)
{
var designer_name = $('input[name="name"]').val();
$.ajax({
type: "GET",
url: "/api/check_brand_exists/",
data : { name : designer_name },
success: function(data) {
var designer_exists = $.parseJSON(data);
// Here and only here you know whether this designer exists
alert(designer_exists);
}
});
}
You could of course perform a synchronous call to the server which is something totally not recommended as it will freeze the client browser and piss the user off your site during the AJAX request by setting the async: false flag:
function checkDesignerName(name)
{
var designer_name = $('input[name="name"]').val();
var designer_exists = false;
$.ajax({
type: "GET",
url: "/api/check_brand_exists/",
async: false,
data : { name : designer_name },
success: function(data) {
designer_exists = $.parseJSON(data);
}
});
return designer_exists;
}
I am mentioning this just for completeness of the answer, not as something that you should ever be doing.
Now because it seems that you are doing some kind of validation logic here, here's what I would recommend you as an ultimate solution: the jquery.validate plugin. It has this great remote rule support that will do exactly what you need here.
$.ajax is a async call. It means the statement return designer_exists gets executed even before success function is executed. That is the reason it always returns false.
your success function don't see designer_exists variable
return action runs before success function will run
You may run sync request or redesign code to callbacks logic.
For sync request your code will be:
var designer_exists = false;
function checkDesignerName(name)
{
designer_exists = false;
var designer_name = $('input[name="name"]').val();
$.ajax( { async:false,
type: "GET",
url: "/api/check_brand_exists/",
data : {name : designer_name },
success: function(data) {
designer_exists = $.parseJSON(data);
}}).statusText;
return designer_exists;
}
As Dimitrov correctly noted it's asynchronous. If you want to encapsulate the ajax call within the function you could pass in the success callback.
function checkDesignerName(name, successCallback)
and then you assign it to the jQuery ajax success function.
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
});