Javascript scoping query using jQuery $.ajax - javascript

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.

Related

JQUERY AJAX WITH CALLBACK [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 9 years ago.
I have an AJAX function that is called from a javascript function.
Something like this:
(CODE1)
//javascript function calling AJAX.
var function check(){
var status = chkHoliday(date,'Date Chosen');
alert('called');
return status;
}
//AJAX function
function chkHoliday(date,str){
var flag = true;
$.ajax({
type: "GET",
url: someurl,
async: false, //if commented, the alert() from the caller function is called before completion of this function.
dataType: "json",
success: {
flag = false;
}
});
return flag;
}
It works well. The only problem is that since async it is set to false, the web page sort of hangs for a while but then continues to proceed further.
To avoid this I read something about callback functions so i tried this out:
(CODE 2)
//javascript function calling AJAX.
var function check(){
var status;
chkHoliday(date,'Date Chosen',function(retVal){
status = retVal;
});
if(status != null){
alert(status);
return status;
}
else{
alert(true);
return true;
}
}
//AJAX function
function chkHoliday(date,str,callback){
var flag = true;
$.ajax({
type: "GET",
url: someurl,
//async: false, //if commented, the alert() from the caller function is called before completion of this function.
dataType: "json",
success: {
flag = false;
callback(flag);
}
});
//return flag;
}
this worked but the alert was called again before the AJAX function could complete stating "undefined". I don't know what I'm doing wrong.
I want, that the AJAX function should wait till it executes completely and then return to the calling function and run the next statements in the caller function with halting the process (i.e with the use of async). Also i want that the value returned by AJAX should be easily accessible to my caller function.
Put the alert inside the callback function:
chkHoliday(date,'Date Chosen',function(retVal){
status = retVal;
if(status != null){
alert(status);
}
else{
alert(true);
}
});
But note you cannot use the return statement anymore as what you have expected because it is asynchronous.
Since AJAX works asynchronous, it is a problem to place it in a function and return a value. To solve this use deferred with a promise. This will promise the ajax result to the caller. It is slightly different. Here is an example. Works like a charm for me.
Of course you will need to adapt it to your needs, but all you really have to do is create your data object.
var data = {}
function chkHoliday(data) {
var deferred = $.ajax({
method: "post",
url: ajaxURL,
dataType: "json",
data: data
});
return deferred.promise();
}
chkHoliday(data).done(function (response) {
console.log(response);
}
return from your php file with a
echo json_encode(array("success"=>"hello"));
Put the alert inside the callback
functions. or else alerts will work simultaneously inspite of success or error.

Jquery .ajax() local variable can't assign to global

I have a jquery ajax code as following:
$(document).ready(function() {
var global_arr = new Array();
$.ajax({
url: 'result.php',
type: 'post',
dataType: 'json',
success: function(data) {
$.each(data, function(key, value) {
global_arr.push(value.name);
});
alert(global_arr); //get correct value, works fine
}
}); //end of ajax function
alert(global_arr); //get null, it doesn't work properly
});
Notice the lines to alert global_arr, why I can't get the value out of $.ajax() function?
Thanks anyone help on this.
Ajax takes time to complete. The function execution does not take nearly as much time. So by the time you get to your alert outside of the ajax request, the ajax request is still using time to complete (either in transmission or in server side actions).
You can always wait for the ajax method to be complete.
$(document).ready(function() {
var global_arr = new Array();
var complete = false;//flag to wait for ajax completion
$.ajax({
url: 'result.php',
type: 'post',
dataType: 'json',
success: function(data) {
$.each(data, function(key, value) {
global_arr.push(value.name);
});
alert(global_arr); //get correct value, works fine
complete = true;//mark ajax as complete
}
}); //end of ajax function
(function runOnComplete(){
if( complete ){//run when ajax completes and flag is true
alert(global_arr);
}else{
setTimeout(runOnComplete,25);//when ajax is not complete then loop
}
})()
});
However, the most common way is to use a callback.
$(document).ready(function() {
function runOnComplete(){//code executes once ajax request is successful
alert(global_arr);
}
var global_arr = new Array();
$.ajax({
url: 'result.php',
type: 'post',
dataType: 'json',
success: function(data) {
$.each(data, function(key, value) {
global_arr.push(value.name);
});
alert(global_arr); //get correct value, works fine
runOnComplete();//callback
}
}); //end of ajax function
});
Ajax is asynchronous. At the time the JS engine reaches your non-functioning alert() line, the AJAX call has not yet had a chance to get a response from the server and set the variable.
That's why the inner alert() works. It gets executed when the response comes in from the server.
Ajax function runs asynchronously and before ajax function adds incoming data in your array, the outer alert function runs and for this reason alerts an empty array.
You can use async-await to make the outer alert function wait for the incoming data.
$(document).ready(async function() {
var global_arr = new Array();
await $.ajax({
url: 'result.php',
type: 'post',
dataType: 'json',
success: function(data) {
$.each(data, function(key, value) {
global_arr.push(value.name);
});
alert(global_arr);
}
}); //end of ajax function
alert(global_arr); //it will work fine now
});
that is because alert(global_arr); //get null, it doesn't work properly runs before $.ajax has completed
My suggestion here would be to break this out in to 3 funcitons so it will make a bit more sense. You will need ajax, handelRequest, onComplete.
You may also want to add and error handler to your ajax function so if it does fail it can do so with out the script locking up on the user.
$(document).ready(function () {
var global_arr = new Array();
$.ajax({
url: 'result.php',
type: 'post',
dataType: 'json',
success: handelRequest(data),
error: handleError
});
function handelRequest(data) {
$.each(data, function (key, value) {
global_arr.push(value.name);
});
onComplete(global_arr); //get correct value, works fine
}
function onComplete(global_arr){
// then here you can do what ever you
// you would like with the array
alert(global_arr);
}
function handleError(){
// gracefully fail
}
})

Returning result from jquery ajax request to a variable rather than firing a function

I'm experimenting with MCV using jquery. I'm making a call to an api, which returns data - what I want to do is return the data to a variable rather than call an additioanl function within my model. The following code doesn't do what I wish though (the_data = result). Any ideas how I can achieve this?
function lookForSomething()
{
var the_data = $.ajax({ type: "GET",
url: TheUrl,
dataType: "jsonp",
success: function(result) { return result; }
});
return the_data;
}
Many thanks,
J
If understand you correctly, you want the data returned by TheUrl to be the return value of the lookForSomething.
Technically, you could do this, with the async option:
function lookForSomething()
{
var the_data;
$.ajax({ type: "GET",
url: TheUrl,
dataType: "jsonp",
async : false,
success: function(result) { the_data = result; }
});
return the_data;
}
I strongly urge you not to do this. It's un-Javascript-like and it will lock up the user's browser while it's running. Much better to pass in a callback to the function and invoke it from success.
You are probably looking for deferred objects:
function lookForSomething()
{
var the_data;
$.when(
$.ajax({ type: "GET",
url: TheUrl,
dataType: "jsonp",
success: function(result) { the_data=result; }
});
).done(function() {
return the_data;
}).fail(function() {
return '';
});
}
Keep in mind that this is still asynchronous, so when you make a call for var ddd = lookForSomething();, ddd will not have the value you expect since the call may still be running. The only reason I brought up $.when() is because it seems like you require a lot of dependencies. $.when() allows you to wait for multiple ajax commands.

Ajax jquery synchronous callback success

I have this function that makes an ajax call. I'm describing the problem in the last chunk of code comments.
function doop(){
var that = this;
var theold = "theold";
var thenew = "thenew";
$.ajax({
url: 'doop.php',
type: 'POST',
data: 'before=' + theold + '&after=' + thenew,
success: function(resp) {
if(resp == 1) {
$(that).siblings('.theold').html(thenew);
}
}
});
// I have some code here (out of the ajax) that **further** changes
// the .theold's html beyond what it was changed inside ajax success
// but the change depends on whether the resp (inside the success
// function) returned 1 or not, so this code out here depends on the ajax
// so it looks like I have to turn this ajax call into a sync ajax
return false;
}
Based on the problem as described in the code comments, what changes are best for this situation?
You need to set async: false for synchronous requests like this:
function doop(){
var that = this;
var theold = $(this).siblings('.theold').html();
var thenew = $(this).siblings('.thenew').val();
$.ajax({
async: false,
url: 'doop.php',
type: 'POST',
data: 'before=' + theold + '&after=' + thenew,
success: function(resp) {
if(resp == 1) {
$(that).siblings('.theold').html(thenew);
}
}
});
// some other code
return false;
}
see here for details
Either set the Ajax call to synchronous as stefita pointed out, or just move your code into the success callback. Why can't you do this? Even if it's another Ajax call it still can be done - you can nest them. With the information given by you so far (I can't see the problematic code, nor I have enough domain knowledge about your project) I don't see a problem, really.
I prefer to use callback to do the job because it achieves exactly the same result without actually making it synchronous. I use success:callback and then pass in the callback as a parameter.
function getData(callback) {
$.ajax({
url: 'register/getData',
data: "",
dataType: 'json',
success: callback
});
}
I then call this function like this:
getData(function(data){
console.log(data); //do something
});

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