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.
Related
I have created a common Javascript file for all my ajax calls. I am trying to use this as a common way to keep track of all ajax calls. Below is the code for the same.
function doAjax(doAjax_params) {
var url = doAjax_params['url'];
var requestType = doAjax_params['requestType'];
var contentType = doAjax_params['contentType'];
var dataType = doAjax_params['dataType'];
var data = doAjax_params['data'];
var beforeSendCallbackFunction = doAjax_params['beforeSendCallbackFunction'];
var successCallbackFunction = doAjax_params['successCallbackFunction'];
var completeCallbackFunction = doAjax_params['completeCallbackFunction'];
var errorCallBackFunction = doAjax_params['errorCallBackFunction'];
//Make the ajax call
$.ajax({
url: getBaseURL() + url,
crossDomain: true,
type: requestType,
contentType: contentType,
dataType: dataType,
data: data,
success: function (data, textStatus, jqXHR) {
console.log(typeof successCallbackFunction);
debugger
//if (typeof successCallbackFunction === "function") {
successCallbackFunction(data);
//}
},
error: function (jqXHR, textStatus, errorThrown) {
if (typeof errorCallBackFunction === "function") {
errorCallBackFunction(errorThrown);
}
}
});
}
This code takes a list of parameters and creates an ajax request based on the parameteres. This code is saved in a file APIHandler.js.
I am trying to call this function from multiple files. An example call is below.
function RedirectToDashboard() {
var params = $.extend({}, doAjax_params_default);
params['url'] = `profile/5`;
params['successCallbackFunction'] = `testsuccess`
doAjax(params);
}
function testsuccess() {
alert("success");
}
When I run this function, I am able to make the call successfully. The only issue comes with the reference to callback function. console.log(typeof successCallbackFunction); returns string instead of function.
I thought maybe order of JS made a difference. I am loading APIHandler.js and then the page specific js. And this ajax call happens at button click, so both JS files are loaded before the ajax call is made.
Other than that, I think maybe I am sending the parameters wrong. That might be causing JS to consider function name as string. But I checked most of the google suggestions on how to pass function, and it seems it needs just the name.
Is there anything else that I might be missing here?
Damn it. I just figured out why it was causing the error. I used quotes when assigning the callback function. Right after posting the question, I realised what was wrong.
params['successCallbackFunction'] = 'testsuccess'
is supposed to be changed to
params['successCallbackFunction'] = testsuccess
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");
}
});
}
Sorry, My first language is not English. I am not sure that if I explain my question properly.
My code is like a main function have two ajax functions (Use ajax function to get foursquare API)
main(){
ajax1();
ajax2();
all other codes
}
the ajax2() function has to get result from ajax1() as input and then return result(actually result was pushed in to global array).
all other codes should be processed after two ajax functions are finished. I tried the asyn: false but it is not working. My html file include newest jquery like this
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js" ></script>
I try the jquery function $.when().done() function and the first ajax works. However, the second ajax() function was in the for loop. The for loop will destroy the mechanism of $.when().done() function:
first ajax: in firstjson function
Second ajax: in transfer function
function firstjson(tmpName,tmpLoc,PhotoJson,foursq){
return $.ajax({
type: 'GET',
url: foursq,
dataType: 'jsonp',
success: function(json) {
for (i = 0; i < 3; i++) {
var resultname = json['response']['venues'][i].name;
var resultlocation = json['response']['venues'][i].location;
var resultlat = resultlocation.lat;
var resultlng = resultlocation.lng;
var tmpmarker = new google.maps.LatLng(resultlat,resultlng)
tmpName.push(resultname);
tmpLoc.push(tmpmarker);
var resultid = json['response']['venues'][i].id;
var tmpPhotoJason = 'https://api.foursquare.com/v2/venues/'+ resultid +'/photos?';
PhotoJson.push(tmpPhotoJason);
}
}
});
}
function transfer(PhotoJson,PhotoURL){
for (i = 0; i < 3; i++) {
return $.ajax({
type: 'GET',
url: PhotoJson[i],
dataType: 'jsonp',
success: function(json) {
resultphoto = json['response']['photos']['items'];
photoprefix = resultphoto[i].prefix;
photopresuffix = resultphoto[i].suffix;
photourl = photoprefix+"150x150" + photopresuffix;
PhotoURL.push(photourl);
}
});
}
}
$.when(firstjson(tmpName,tmpLoc,PhotoJson,foursq)).done(function(){
alert("test1");
$.when(transfer(PhotoJson,PhotoURL).done(function(){
console.log(PhotoURL);
all other codes!!!!
});
});
//PhotoURL is global array
So the first "when" function work properly. alert("test1") work after the firstjson was done. However the for loop inside transfer function will break the when function. How can I fix the problem. Please help me. I will appreciate you can give me any related information. Thanks!!!
This will execute ajax2 after ajax1
function anotherMethod(){
//Here you do all that you want to do after the last $ajax call
}
main(){
firstjson(tmpName,tmpLoc,PhotoJson,foursq)
.then(transfer(PhotoJson,PhotoURL))
.then(anotherMethod);
}
As you are returning a promise from the first with the "return $ajax..."
So you organice your code like this:
in methods with ajax calls you return the call as you are doing now
return $.ajax();
that returns a promise that you chain.
And you put what you want to do in another method so you call it in the last "then".
Non-Blocking Example
You should use non-blocking code. You can turn async off (async: false) but this can easily be done in a non-blocking manor using callback functions.
function main(){
$.ajax({ // First ajax call (ajax1)
url: "first/ajax/url",
type: "GET", // POST or GET
data: {}, // POST or GET data being passed to first URL
success: function(x){ // Callback when request is successfully returned
// x is your returned data
$.ajax({ // Second ajax call (ajax2)
url: "second/ajax/url",
type: "GET", // POST or GET
data: {
thatThing: x
}, // POST or GET data passed to second URL
success: function(y){
// y is your second returned data
// all other codes that use y should be here
}
});
}
})
}
This would be the non-blocking approach, nest your function within "success" callback functions. Nest ajax2 within ajax1's "success" callback to ensure that ajax2 is not executed before ajax1 has returned and nest your "all other codes" inside the "success" callback of ajax2 to ensure they are not executed until ajax2 has returned.
Blocking Example
If you absolutely must (please avoid at all cost) you can disable async which will block all JavaScript code from executing until the ajax has returned. This may cause your browser to temporarily freeze until the ajax request has returned (depending on the browser).
function main(){
var x = ajax1();
var y = ajax2(x);
window["y"] = y; // push to global as you requested but why?
// All other codes that can now use y
}
function ajax1(){
var x;
$.ajax({
url: "first/ajax/url",
async: false,
type: "GET", // POST or GET,
data: {}, // POST or GET data being passed to first URL
success: function(r){x=r}
});
return x;
}
function ajax2(x){
var y;
$.ajax({
url: "second/ajax/url",
async: false,
type: "GET", // POST or GET,
data: {
thatThing: x
}, // POST or GET data being passed to second URL
success: function(r){y=r}
});
return y;
}
Once again I stress, try not to disable async that will cause your code to block and is BAD code. If you absolutely 100% have to for some reason than than it can be done but you should attempt to learn how to write non-blocking code using callbacks as the first example does.
Social Network Example
Now I'll do an example of an ajax call to get an array of your friends IDs, and then a series of ajax calls to get each of your friends profiles. The first ajax will get the list, the second will get their profiles and store then, and then when all profiles have been retrieved some other code can be ran.
For this example, the url https://api.site.com/{userID}/friends/ retrieves an Object with a list of friends IDs for a particular user, and https://api.site.com/{userID}/profile/ gets any users profile.
Obviously this is a simplified api as you will probably need to first establish a connection with a apikey and get a token for this connection and the token would likely need to be passed to the api uris but I think it should still illustrate the point.
function getFriends(userID, callback){
$.ajax({
url: "https://api.site.com/"+userID+"/friends/",
success: function(x){
var counter = 0;
var profiles = [];
for(var i=0;i<x.friendIDs.length;i++){
$.ajax({
url: "https://api.site.com/"+x.friendIDs[i]+"/profile/",
success: function(profile){
profiles.push(profile);
counter++;
if(counter == x.friendIDs.length) callback(profiles);
}
});
}
}
});
}
getFreinds("dustinpoissant", function(friends){
// Do stuff with the 'friends' array here
})
This example is "Non-blocking", if this example were done in a "blocking" way then we would ask for 1 friends profile, then wait for its response, then request the next and wait and so on. If we had hundreds of friends you can see how this would take a very long time for all ajax calls to complete. In this example, which is non-blocking, all requests for profiles are made at the same time (within 1ms) and then can all be returned at almost exactly the same time and a counter is used to see if we have gotten responses from all the requests. This is way way way faster than using the blocking method especially if you have lots of friends.
I'm trying to make multiple calls to Ajax, i have fields like time intervals and no of calls to ajax under that time period. Now the problem is, while making multiple calls to same Ajax, there may be chances of merging of data with the other data that were send to Ajax earlier. I am not sure that it will happen.
Here my Ajax call.
callAjax = function () {
var dataIn = inObj.data || {};
var successFunc = inObj.success || function () {};
var passOn = inObj.passOn || {};
var myParams = {drape:1,type:'GET'};
myParams.url = this.homeComingUrl;
$.extend(myParams,params);
var data = this.fillAction(action,dataIn);
if (myParams.drape) { vidteq.utils.drapeSheer(action); }
var that = this;
var magicCall = $.ajax({
url:myParams.url,
type:myParams.type,
data:data,
success: function (response) {
// TBD we need better error handling
if (myParams.drape) { vidteq.utils.undrapeCurtain(action); }
successFunc(response,passOn);
},
error:function(response) {
if (myParams.drape) { vidteq.utils.undrapeCurtain(action); }
that.gui.io.handleError(response);
}
});
}
saveEvents = function () {
this.commitEditingEvent();
var dataEvents = this.collectEventsToSave();
//$('#calendar').fullCalendar('removeEvents');
var that = this;
if (vidteq.eTrainer==1) {
dataEvents = arguments[0];
}
if (!dataEvents.length) { alert("Nothing to save");return; }
this.callAjax('updateEvents',{
data : { events : JSON.stringify(dataEvents) },
success : function (response,passOn) {
that.handleGetEvent(response,passOn);
}
},{type:'POST'});
}
This may not be required for understanding the problem.
If any body can explain how Ajax handles multiple calls, then it'll really helpful.
First line, your anonymous function isn't saved and isn't ran. Then. In each function, what does this refer to ? What is this context ? Is this window or do you call your function like saveEvents.apply( jQuery ) ?
JavaScript is powerful, when your want to run XMLHttpRequest (Ajax uses it), scripts are called when an event happen, like "server is found", "request is send", "file is reading", "file loaded"... for each state of your request. Ajax by jQuery help you to request asynchronous. You can request as many Ajax request as you would like in the same time. The important is to create a function happen in success case.
In this success function, you receive data, you compute it, then this function may call another Ajax request, and so on. When you chain requests like this to get the same file, we call it Ressource.
Ressource uses Ajax which uses XMLHttpRequest.
you need to do asynic :false in your ajax method
function isLoggedIn() {
var isLoggedIn;
$.ajax({
async: false,
// ...
success: function(jsonData) {
isLoggedIn = jsonData.LoggedIn
}
});
return isLoggedIn
}
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)