Asynchronous Ajax Logic - javascript

$("#container").on("change", "#control1", function() {
if ($("#checkData").val()) {
$.get("/Controller/CheckData/" + $("#control2").val(), function(data1) {
if(!data1.Success) {
alert("Unable to do POST.");
return;
});
};
formData = $("#form").serialize();
$.post("/Controller/PostData", formData, function(data2) {
// Do something...
});
}
If checkData is false, the form should post. If checkData is true, the form should only post if the get returns true.
This doesn't seem to work because the form gets posted while the alert dialog is still open. I think it may be due to the asynchronous nature of AJAX. Is this correct?

Yes. When you call the $.get() method, the code continues executing. That means that it immediately goes to the declaration and $.post() call that follow. If you want to wait to execute those until after the $.get() call completes, you'll need to put them in the callback function.

yes, the post will always happen because you haven't done anything to prevent it. If you want the post to be sent or not sent based on the outcome of the $.get, you'll need to move it to the get callback fn.
Additionally, your code was riddled with syntax errors, though I suspect many of them were copy paste errors otherwise you wouldn't have even been getting the alert.
$("#container").on("change", "#control1", function () {
if ($("#checkData").val()) {
$.get("/Controller/CheckData/" + $("#control2").val(), function (data1) {
if (!data1.Success) {
alert("Unable to do POST.");
} else {
var formData = $("#form").serialize();
$.post("/Controller/PostData", formData, function (data2) {
// Do something...
});
}
});
}
});

Yes, you'll allways get to $.post...

Yes. And you cannot return from a callback. You will need to do
$("#container").on("change", "#control1", function () {
if ($("#checkData").val()) {
var formData = $("#form").serialize();
$.get("/Controller/CheckData/" + $("#control2").val(), function (data1) {
if (!data1.Success)
alert("Unable to do POST.");
else
$.post("/Controller/PostData", formData, function (data2) {
// Do something...
});
});
}
});
Btw, it might be unsafe to rely on a client-side check of the data (even if by ajaxing /CheckData/) before "allowing" a POST request. If you want a two-step process, you should return a "safety" token from the GET request.

Please note that these two lines are reversed in your code (and therefore don't match up):
$("#container").on("change", "#control1", function() {
if ($("#checkData").val()) {
var c2 = $("#control2").val();
$.get("/Controller/CheckData/" + c2, function(data1) {
if(!data1.Success) {
alert("Unable to do POST.");
return;
} <=== HERE
}); <=== HERE
};
formData = $("#form").serialize();
$.post("/Controller/PostData", formData, function(data2) {
// Do something...
});
}
I extracted var c2 from the $.get line only to see the braces more easily, not for any other reason.

Related

I can't seem to break out of A $.each() loop

I can't seem to manage to break out of my each loop if the ajax returns an error. I've tried
return false;
and other similar thing but the $.each still continues to run.
I need to be able to do this so that I can display error messages from my back end after posting it via ajax(I know this is bad practice however a client needed to be able to be able to send multiple forms off at once.).
Can anyone explain what I've done wrong?
var postAll = function(button_clicked)
{
e.preventDefault();
var form_response = [];
var formsCollection = document.getElementsByTagName("form");
$.each(formsCollection, function (key, value)
{
console.log(value.action);
console.log(value.id);
var url = value.action;
var id = value.id;
var data = ($('#' + id + '').serialize());
if (id == 'additionalInfo')
{
data = {'Add_info': $('#Add_info').val(),};
}
if (id != 'DONE')
{
$.ajax({
type: "POST",
dataType: 'json',
url: url,
beforeSend: function (xhr)
{
xhr.setRequestHeader('X-CSRF-TOKEN',$("#token").attr('content'));
},
data: data,
success: function (data)
{
console.log('success'); // show response from the php script.
form_response.push(data); // show response from the php script.
},
error: function (data)
{
console.log('fail'); // show response from the php script.
display_errors(data, id); // show response from the php script.
return true;
}
});
}
});
}
AJAX is asynchronous, when executing your $.each function it will execute the AJAX call and "Not wait" for the others to finish
To solve your problem you'll have to write a function that will execute the first ajax call and in the success it will execute itself again with the second ajax call.
Example:
var data = [form1,form2...etc];
function letsLoop(data,index = 0){
$.ajax({
url:....
success: function(){
letsLoop(data,index+1);
},
error: function(){
}
});
}
and here you call your function:
letsLoop(data,0);
If by breaking out of the loop you mean the return in your error handler, then it won't work as you think it would.
Your loop creates asynchronous requests 'at once'. Then each of these requests is handled by the browser (more or less simultaneously), then come responses. So by the time your error handler runs the loop has long finished.
BTW, the return in your case relates to the error handler, not the function inside the loop.
So, to achieve what you want you should 'queue' your AJAX requests and perform them one by one.
One possible solution is to create an array of forms then take (and remove it from the array) the first one, perform a request, on a response repeat the whole thing, and keep repeating until the array is empty.

generalizing ajax call into function

I'm trying to attempt to generalize my ajax calls into a function as follows. I have not done this before and am not sure sure if I'm doing it correctly.
<script>
$(document).ready(function(){
var reg_no=$("#reg_no").val();
reg_no=reg_no.trim();
if(reg_no!==""){
//populate fields
data={reg_no:reg_no,func:"getSupplierDetails"};
success_function="updateFormFields";
ajax_call(data,success_function);
}
});
function ajax_call(data,success_function){
$.ajax({
type:"POST",
url:"../control/supplier-c.php",
dataType:"json",
data:data,
success:function(data){
success_function(data); //variable function works??
}
});
}
function updateFormFields(data){
//some code here to handle data array
}
</script>
What I'm trying to do here is avoid rewriting the whole ajax code by passing the data array and the function to be executed on success. What I'm not sure is the use of variable functions as i have done.
A note to be made is that the whole thing works for an ajax call if updateFormFields() code was moved into the success handler in the ajax call and the ajax_call() was not defined as a seperate function but implemented right after the comment "populate fields". I just have no experience in trying it this way and I need to know if this is possible or not.
Thank You
In Javascript, functions are first class objects, meaning you can pass them around as parameters.
function json_post(url, data, success_function, error_function) {
$.ajax({
type:"POST",
url:url,
dataType:"json",
data:data
}).then(success_function, error_function);
}
Then you can call it as
json_post("../control/supplier-c.php", { data: "data" }, function (res) {
console.log('Ajax req successful');
console.log(res);
}, function (res) {
console.log('Error in ajax req');
console.log(res);
});
In your case, you can do:
ajax_call(data, updateFormFields);
and it should work as expected.
There's no need to wrap the success function, you can just apply apply it directly.
function ajax_call(data, success_function) {
$.ajax({
...
success: success_function
});
}
An even better idea is to avoid the legacy success and error callbacks and instead return the jQuery promise. You can use standard promise methods .then() and `.
function ajax_call(data) {
return $.ajax({
...
});
}
ajax_call()
.then(function(data) {
// this runs if it succeeds
})
.fail(function(err) {
// this runs if it failed
});
Promises have a huge benefit to being chain-able, making the code flatter, avoiding the nest of "christmas tree callbacks".
I would recommend checking success_function as well as failure_function to handle server response (XHR) errors also.
function success_function(){
//code to handle success callback
}
function error_function(){
//code to handle failure callback
}
function ajax_call(data, success_function, error_function) {
if (typeof success_function === 'function' && typeof error_function === 'function') {
$.ajax({
type: "POST",
url: "../control/supplier-c.php",
dataType: "json",
data: data,
}).then(success_function).fail(error_function);
}
}

jQuery : How can I call $.ajax when a particular condition is met in the nested ajax calls scenario?

Updated Question with Code
I have a situation where I am calling two nested ajax calls one after another. The first ajax call submits a form without the attachment. The result of the first ajax call will create a requestId and using second ajax call I have to attach multiple attachments to the created requestId.
The result of below code, both first and second ajax calls are being called N times of attachment. For ex:- If there are 3 attachments, createRequestId ajax call(first ajax call) called 3 times which creates 3 requestIds. My issue is, createRequestId ajax call needs to be called only one time (first time) and during rest of the loop, only the second ajax call should be called. How can I achieve this in the below code?
Current situation
RequestId 1,Attachment 1
RequestId 2,Attachment 2
RequestId 3, Attachment 3
Expected output
RequestId 1, Attachment 1, Attachment 2, Attachment 3
//loop through number of attachments in the form
$("#myDiv").find("input[type=file]").each(function(index,obj) {
var fObj = $(obj),
fName = fObj.attr("name"),
fileDetail = document.getElementById(fName).files[0];
//FileSize Validation
if(fileDetail !=undefined && fileDetail !=null)
{
if(fileDetail.size > 5*Math.pow(1024,2))
{
alert("Please upload the attachment which is less than 5 MB");
return false
}
}
$.ajax({ //First Ajax Call
url: 'http://..../createRequestId'
type:'POST'
data: stringify(formData)
success: function(resObj){
$("#showResponseArea span").removeClass("hide");
$("#showResponseArea span").removeClass("alert-success");
var requestId = resObj.requestId;
if(requestId>1 && fileDetail !=undefined && fileDetail !=null) {
$.ajax({ //Second Ajax Call
url: 'http://..../doAttach?fileName=' + fileDetail.name +
'&requestId=' +requestId,
type:'POST',
data: fileDetail,
success: function(resObj){
alert("Attachment Successful");
}
error : function(data) {
alert("Failed with the attachment");
}
});
}
},
error: funciton(resObj) {
alert("Some Error Occured");
}
});
});
I know this doesn't really answer your question in full, but if you don't mind me offering a little constructive code review. It's hard to really manage and debug code when it's all thrown into one big function with many lines, especially if you're nesting async calls (you're getting close to nested callback hell). There's a reason code like this can get hard to maintain and confusing.
Lets incorporate some Clean Code concepts which is to break these out into smaller named functions for readability, testability, and maintainability (and able to debug better):
First you don't need all those !== and undefined checks. Just do:
if (fileDetail)
and
if(requestId>1 && fileDetail)
that checks for both null and undefined on fileDetail.
Then I’d start to break out those two ajax calls into several named functions and let the function names and their signatures imply what they actually do, then you can remove unnecessary comments in code as well as once you break them out, typically you can find repeated code that can be removed (such as redundant post() code), and you will find that code you extracted out can be tested now.
I tend to look for behavior in my code that I can try to extract out first. So each one of those ​if​ statements could easily be extracted out to their own named function because any if statement in code usually translates to "behavior". And as you know, behavior can be isolated into their own modules, methods, classes, whatever...
so for example that first if statement you had could be extracted to its own function. Notice I got rid of an extra if statement here too:
function validateFileSize(fileDetail)
if(!fileDetail && !fileDetail.size > 5*Math.pow(1024,2)){
alert("Please upload the attachment which is less than 5 MB");
return false
};
};
So here's how you could possibly start to break things out a little cleaner (this could probably be improved even more but here is at least a start):
$("#myDiv").find("input[type=file]").each(function(index,obj) {
var fObj = $(obj),
fileName = fObj.attr("name"),
file = document.getElementById(fileName).files[0];
validateFileSize(file);
post(file, 'http://..../createRequestId');
});
// guess what, if I wanted, I could slap on a test for this behavior now that it's been extracted out to it's own function
function validateFileSize(file){
if(!file && !file.size > 5*Math.pow(1024,2)){
alert("Please upload the attachment which is less than 5 MB");
return false
};
};
function post(url, data){
$.ajax({
url: url,
type:'POST',
data: stringify(data),
success: function(res){
showSuccess();
var id = res.requestId;
if(id > 1 && file){
var url = 'http://..../doAttach?fileName=' + file.name + '&requestId=' + id;
postData(file, url);
}
},
error: function(err) {
alert("Some Error Occurred: " + err);
}
});
// I didn't finish this, and am repeating some stuff here so you could really refactor and create just one post() method and rid some of this duplication
function postData(file, url){
$.ajax({
url: url,
type:'POST',
data: file,
success: function(res){
alert("Attachment Successful");
},
error : function(data) {
alert("Failed with the attachment");
}
});
};
// this is behavior specific to your form, break stuff like this out into their own functions...
function showSuccess() {
$("#showResponseArea span").removeClass("hide");
$("#showResponseArea span").removeClass("alert-success");
};
I'll leave it here, next you could get rid of some of the duplicate $ajax() code and create a generic post() util method that could be reused and move any other behavior out of those methods and into their own so that you can re-use some of the jQuery ajax call syntax.
Then eventually try to incorporate promises or promises + generators chain those async calls which might make it a little easier to maintain and debug. :).
I think your loop is simply in the wrong place. As it is, you're iterating files and making both AJAX calls once.
Edit: I now show the appropriate place to do extra validations before the first AJAX call. The actual validation was not part of the question and is not included, but you can refer to JavaScript file upload size validation.
var fileSizesValid = true;
$("#myDiv").find("input[type=file]").each(function(index, obj) {
// First loop checks file size, and if any file is > 5MB, set fileSizesValid to false
});
if (fileSizesValid) {
$.ajax({ //First Ajax Call
url: 'http://..../createRequestId',
type: 'POST',
data: stringify(formData),
success: function(resObj) {
var fObj = $(obj),
fName = fObj.attr("name"),
fileDetail = document.getElementById(fName).files[0];
//loop through number of attachments in the form
$("#myDiv").find("input[type=file]").each(function(index, obj) {
$("#showResponseArea span").removeClass("hide");
$("#showResponseArea span").removeClass("alert-success");
var requestId = resObj.requestId;
if (requestId > 1 && fileDetail != undefined && fileDetail != null) {
$.ajax({ //Second Ajax Call
url: 'http://..../doAttach?fileName=' + fileDetail.name +
'&requestId=' + requestId,
type: 'POST',
data: fileDetail,
success: function(resObj) {
alert("Attachment Successful");
},
error: function(data) {
alert("Failed with the attachment");
}
});
}
})
},
error: function(resObj) {
alert("Some Error Occured");
}
});
}
As a side note, take care where you place your braces. In JavaScript your braces should always be at the end of the line, not the start. This is not a style preference thing as it is most languages, but an actual requirement thanks to semicolon insertion.
Try following code (Just a re-arrangement of your code and nothing new):
//loop through number of attachments in the form
var requestId;
$("#myDiv").find("input[type=file]").each(function(index,obj) {
var fObj = $(obj),
fName = fObj.attr("name"),
fileDetail = document.getElementById(fName).files[0];
//FileSize Validation
if(fileDetail !=undefined && fileDetail !=null)
{
if(fileDetail.size > 5*Math.pow(1024,2))
{
alert("Please upload the attachment which is less than 5 MB");
return false
} else if(!requestId || requestId <= 1){
$.ajax({ //First Ajax Call
url: 'http://..../createRequestId'
type:'POST'
data: stringify(formData)
success: function(resObj){
$("#showResponseArea span").removeClass("hide");
$("#showResponseArea span").removeClass("alert-success");
requestId = resObj.requestId;
secondAjaxCall(fileDetail);
},
error: funciton(resObj) {
alert("Some Error Occured");
}
});
} else if(requestId>1) {
secondAjaxCall(fileDetail);
}
}
});
function secondAjaxCall(fileDetail) {
$.ajax({ //Second Ajax Call
url: 'http://..../doAttach?fileName=' + fileDetail.name +
'&requestId=' +requestId,
type:'POST',
data: fileDetail,
success: function(resObj){
alert("Attachment Successful");
}
error : function(data) {
alert("Failed with the attachment");
}
});
}

Checking a Url in Jquery/Javascript

All I need is a method that returns true if the Url is responding. Unfortunately, I'm new to jQuery and it's making my attempts at writing that method rather frustrating.
I've seen several examples of jQuery using .ajax, but the code is consistently failing on me. What's wrong?
var urlExists = function(url){
//When I call the function, code is still executing here.
$.ajax({
type: 'HEAD',
url: url,
success: function() {
return true;
},
error: function() {
return false;
}
});
//But not here...
}
That isn't how AJAX works. AJAX is fundamentally asynchronous (that's actually what the first 'A' stands for), which means rather than you call a function and it returns a value, instead you call a function and pass in a callback, and that callback will be called with the value.
(See http://en.wikipedia.org/wiki/Continuation_passing_style.)
What do you want to do after you know whether the URL is responding or not? If you intended to use this method like this:
//do stuff
var exists = urlExists(url);
//do more stuff based on the boolean value of exists
Then what you instead have to do is:
//do stuff
urlExists(url, function(exists){
//do more stuff based on the boolean value of exists
});
where urlExists() is:
function urlExists(url, callback){
$.ajax({
type: 'HEAD',
url: url,
success: function(){
callback(true);
},
error: function() {
callback(false);
}
});
}
urlExists() can not return because it needs wait for the request.
Either pass it a callback, or make it synchronous (not recommended, because it locks the browser).
var urlExists = function(url, callback) {
if ( ! $.isFunction(callback)) {
throw Error('Not a valid callback');
}
$.ajax({
type: 'HEAD',
url: url,
success: $.proxy(callback, this, true),
error: $.proxy(callback, this, false)
});
};
Then you can do
urlExists('/something', function(success) {
if (success) {
alert('Yay!');
} else {
alert('Oh no!');
}
});
It also worth mentioning the same origin policy.
Also, returning from an anonymous function's scope will not return in the parent function (like in your original example). It just returns that inner function. To return from an inner to a parent, set a flag and return it.
Basically, there is nothing wrong with your code. See it work here:
http://jsfiddle.net/PK76X/
My guess is that you're using it to check the availability of content on a different domain, which fails because browsers don't allow cross domain ajax-requests.
If the url is from the same domain as your page you can do it. But if it is from a different domain, for example google.com, then it will fail due to cross domain security.
In general, you should probably run your script in Firefox using the firebug plugin. It will give you the details needed to solve the issue.
The ajax and post methods are asynchronous, so you should handle the result in a callback method.
AJAX is basically asynchronous, and that's why the behavior you are describing.
I've used the following, which is free of cross origin, to get a simple true/false indication whether a URL is valid, in a synchronous manner:
function isValidURL(url) {
var encodedURL = encodeURIComponent(url);
var isValid = false;
$.ajax({
url: "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20html%20where%20url%3D%22" + encodedURL + "%22&format=json",
type: "get",
async: false,
dataType: "json",
success: function(data) {
isValid = data.query.results != null;
},
error: function(){
isValid = false;
}
});
return isValid;
}
The usage is then trivial:
var isValid = isValidURL("http://www.wix.com");
alert(isValid ? "Valid URL!!!" : "Damn...");
Hope this helps

JQuery .get function vars

Once again having problems being a new to javascript. I want to get the reply from my PHP script using JQuery's get. Here's my function
function getNext(){
var returnData = null;
$.get("get.php", {task:'next'}, function(responseText){
returnData = responseText; }, "text");
return returnData;
}
Now obviously the function returns null even though the request was successful. Why?
Thanks for your help in advance.
Because ajax is asynchronous, it acts independently on its own and so by the time the return statement completes, it hasn't been assigned yet.
function getNext(){
var returnData;
$.ajax({
url: "get.php",
data: {task:'next'},
success: function(responseText) { returnData = responseText },
async: false;
});
return returnData;
}
Note that this may "freeze" the UI because Javascript is single threaded -- it will wait until it receives the response from the server.
You may want to refactor it so that the action is called in the success callback. Could you show us what triggers the invokation of the getNext method?
try this
function getNext(){
var returnData = null;
$.get("get.php", {task:'next'},
function(responseText){
returnData = responseText;
alert('Data ' + responseText);
});
}
You will have to poll whether returnData contains actual data that you want periodically (or put your code into the callback method)

Categories

Resources