I am having problems understanding how I can wait for the results of multiple functions that have ajax in them.
I tried using Promise.all() or $.when().done(); These functions only call another function when they are done and I would like to avoid this method because it will infinitely complicate may code.
I also don't want to use async: false in the ajax call.
The main function doesn't even have too look like that.
I only need to know if there is a method that can call 1 or more function that have ajax then wait for the results without continuing in another function.
function main(){
//some functions
UploadFile('input_1');
UploadFile('input_2');
.
.
.
UploadFile('input_n');
//Here is where I want to know all the results of the UploadFile functions
//some other functions
//return true or false dependig on //some functions, UploadFile AND //some other functions
}
function UploadFile(inputId){
return $.ajax({
//ajax parameters
success: function(IDUpload) {
if (IDUpload > 0) {
return true;
}
return false;
},
error: function(error) {
return false;
}
});
}
Edit:
The main() function is the validation function for the form. It seems that if I make it async, it wil not fire at all it will not wait for then UploadFile calls.
You can use the await prefix, to make any asynchronous function or result to be resolved synchronously, by halting the function until the promise resolved to either success or an error.
To use the await prefix, you need to declare the function containing the await prefixes with async, for the runtime to prepare for the possible situation to await for a promise.
Further Information can be read in the MDN Documentation:
Await Documentation
// We need to define the Function as an asynchronous function, to apply await inside
async function main(e){
// Added for preventing the submit to propagate to the action-URL
e.preventDefault();
e.stopPropagation();
// As the UploadFile now only uses the synchronous Ajax-Call we can just use the returning
// result of the Function.
let res = UploadFile('input_1');
console.log(res);
// ... Other code
}
// We use the async: false Property of the Jquery Ajax Call, thous getting the response as a
// result.
function UploadFile(inputId){
let res = $.ajax({
method: "GET",
async: false,
url:"https://www.random.org/integers/?num=1&min=-10&max=10&col=1&base=10&format=plain&rnd=new",
});
// As the Ajax Call is now synchronous we have to check if the Request was successfull
// ourself. For this, we check for the Response status. Any status between 200 - 299 is
// successfull and any other has any error depending on the status code. But we wont bother
// much which error was thrown.
if(res.status >= 300 || res.status < 200) {
console.error("Request Error:", res.statusText)
return false;
}
// With the guarding If-Check before we can surely assume, the Request was successfull and
// can check now with the same logic as in the success-Method before. I've inverted the
// checks to keep consitent with the guarding If-statements.
// Same as: if(IDUpload < 0)
if(res.responseText < 0) {
console.log("The Response is not 'valid'");
return false;
}
console.log("The Response is 'valid'");
return true;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form onsubmit="main(event)">
<button type="submit">Submit Form</button>
</form>
Related
I got following code :
$.ajax({
type: "POST",
async: false,
url: "CheckIdExist",
data: param,
success: function(result) {
if (result == true){
return false;
}
},
error: function(error) {
alert(error);
return false;
}
});
if ajax return value is true, form needs to stop submit.
but it does not stopping submit form.
any help please.
I assume you have something like:
form.submit(function(event) {
$.ajax(...);
});
You want to return false (or call event.preventDefault()) in the event handling function itself, and not in the AJAX call, such as:
form.submit(function(event) {
event.preventDefault();
$.ajax(...);
});
A slight variation of this question is:
I want to use jquery and ajax to present an error message to a user,
but then to do normal processing if there is no error.
Suppose method "check" returns a response that is empty if there is no error, and has the error(s) in it if there are any.
Then you can do something like this in your ready function:
$("#theform").submit(function() {
var data = { ... }
$.get('/check', data,
function(resp) {
if (resp.length > 0) {
$("#error").html(resp);
} else {
$("#theform").unbind('submit');
$("#theform").submit();
}
});
event.preventDefault();
});
So, how does it work? When the outer submit function is triggered, it builds the data needed for the AJAX call (here the higher level function get). The call is scheduled, and the main thread of operation immediately continues, but the submit is unconditionally stopped, so nothing apparent happens.
Some time passes, and the AJAX call returns the result of the request. If non-empty, the result is shown to the user.
If the response is empty, however, the submit function is unbound. This prevents the ssytem from making an extra call through the outer submit function. The inner submit function is called. This triggers an actual form submission to the form's target, and life goes on as expected.
You need to do a callback.
This entry in the FAQ helped me a lot when I had this exact problem.
getUrlStatus('getStatus.php', function(status) {
alert(status);
});
function getUrlStatus(url, callback) {
$.ajax({
url: url,
complete: function(xhr) {
callback(xhr.status);
}
});
}
The reason for that is that you can not return in an AJAX function.
The code above does not work as desired due to the nature of asynchronous programming. The provided success handler is not invoked immediately, but rather at some time in the future when the response is received from the server. So when we use the 'status' variable immediately after the $.ajax call, its value is still undefined.
You can't put this code in a function or in the onsubmit of a form, the success function returns it's result returning to the jQuery ajax context, NOT the form submit context.
In this case you need to perform a synchronous http request, i.e. you have to set the async option to false.
In your version the httpxmlrequest is asynchronous. It might be finished long after your onsubmit handler has returned and the onsuccess callback is invoked out of context of the onsubmit handler.
The "return false" will be the return value of the anonymous function function(result) {... } and not the return value of the onsubmit handler.
I had this problem also but solved it by changing the input type="submit" to type="button" and then just do your ajax request
$("input#submitbutton")
{
$.ajax(
{ type: "POST",
async: false,
url: "CheckIdExist",
data: param,
success: function(result) {
if (result == true){
//TODO: do you magic
}
else
$("form").submit();
},
error: function(error) {
alert(error);
return false;
}
});
});
Here is some code that takes a Backbone model, saves it and then waits for the response and fires the jQuery .done() or .fail() code. This is working fine but on fail we actually want to get the returned message from the service add it to our errors object. This is all within a Backbone validate() function; after this code, we check the errors object and display the message if there is one.
It seems like if we .fail(), everything stops executing. We need to continue the validate function. I found this question/answer but it didn't seem to make a difference: Is there a way to continue after one deferred fails?
Any way to continue executing code after hitting a deferred.fail()?
addressModel.save().done(function() {
console.log("promise done");
model.set("...", false);
}).fail(function(response) {
console.log("promise fail");
if (response.responseJSON && response.responseJSON._messages) {
_.each(response.responseJSON._messages, function(value, key) {
errors[key] = value[0];
});
}
});
It's possible but tricky - at least until 3.0. The trick is:
Don't use .fail use .then.
Return a resolved deferred from the fail.
This is like signalling that we've dealt with the exception we got here:
var p = addressModel.save().then(function() {
console.log("promise done");
model.set("...", false);
}, function(response) {
console.log("promise fail");
if (response.responseJSON && response.responseJSON._messages) {
_.each(response.responseJSON._messages, function(value, key) {
errors[key] = value[0];
});
}
return $.Deferred().resolve(); // return a resolved deferred
});
This will let you do:
p.then(function(){
// this code runs when either failure or success happened, can also chain
});
We never could get this to work with a promise, whether it was returned by a function within Backbone's validate() or by validate() itself. But I found a solution within Backbone itself--save() will accept async: false and stall execution until a response is received, then continue from there. It probably uses a promise to do this behind the scenes.
addressModel.save(null, {
async: false,
success: function() {
model.set("...", false);
},
error: function(model, response) {
if (response.responseJSON && response.responseJSON._messages) {
_.each(response.responseJSON._messages, function(value, key) {
errors[key] = value[0];
});
}
}
});
var voted;
$.getJSON("http://smart-ip.net/geoip-json?callback=?", function(data){
voted = data.host;
console.log(voted);
});
console.log(voted);
So voted is undefined outside, however, it's defined properly inside the function.
I'm wondering how I can use the data.value outside the function. The only thing I can think of is using a global variable, however, it doesn't work as expected.
Edit: Need the IP outside of functions
$.getJSON("http://smart-ip.net/geoip-json?callback=?", function(data){
voted(data.host);
});
function voted(ip) {
ip_voted = ip_array.indexOf(ip);
if (ip_voted > -1) {
window.location.href = "results";
}
}
if (choice && ip == '123123123') {
}
you can use this way
$.getJSON("http://smart-ip.net/geoip-json?callback=?", function(data){
writeVoted(data.host);
});
function writeVoted(voted) {
console.log(voted);
}
or leave a synchronous call (I believe not be advisable)
async (default: true)
Type: Boolean
By default, all requests are sent asynchronously (i.e. this is set to true by default). If you need synchronous requests, set this option to false. 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().
The reason it doesn't work out for you is that the call to getJSON is an asynchronous call. In sense it means that it is ripped out of the current flow and executed somewhere independently. Take a look at the comments I have added to your code below:
var voted;
//getJSON is executed right away - async
$.getJSON("http://smart-ip.net/geoip-json?callback=?", function(data){
//This callback function executes when the remote request responds. The timeframe is variable.
voted = data.host;
console.log(voted);
});
//Gets executed right away - doesn't wait for getJSON to get the remote response.
//voted is probably still undefined.
console.log(voted);
What you need to to is add a function that continues your flow, as shown below:
$.getJSON("http://smart-ip.net/geoip-json?callback=?", function(data){
voted(data.host);
});
function voted(ip) {
ip_voted = ip_array.indexOf(ip);
if (ip_voted > -1) {
window.location.href = "results";
}
verify(ip);
}
function verify(ip) {
if (choice && ip == '123123123') {
}
}
If you really need to use the data in a global variable, you could "wait" for the response - however I strongly advise against doing this:
var host, value;
var responseArrived = false;
$.getJSON("http://smart-ip.net/geoip-json?callback=?", function(data){
host = data.host;
value = data.value;
responseArrived = true;
});
while( !responseArrived ) {
//NOT A GOOD IDEA!
//Will continue to loop until the callback of getJSON is called.
//NOT A GOOD IDEA!
}
console.log(host);
console.log(value);
I have the following data
var data = [{user:"somename"}, {user:"anothername"}];
I have this function that processes that that, let say checking user if it exist
function process(user) {
$.ajax({
url: 'http://domain.com/api',
success: function(result) {
if(result == 'success')
anotherFunction();
else
console.log('error');
}
})
}
function anotherFunction() {
$.ajax({
// do stuffs
})
}
$.each(data, function(k,v){
process(v.user);
})
The $.each will try to loop even the process and the anotherFunction hasn't finished yet
Question, what can I do to assure that all functions are finished executing before moving on to another index?
I heard I can use jquery deferred.
Collect the promises returned by your AJAX function, if necessary post-processing that result with .then so that it calls anotherFunction() which must also return the result of $.ajax.
function process() {
return $.ajax(...).then(function(result) {
if (result === 'success') {
return anotherFunction();
} else {
return null; // this might need to change...
}
});
}
function anotherFunction() {
return $.ajax(...);
}
var promises = [];
$.each(data, function(k, v) {
promises.push(process(v.data));
}
and then wait for all the promises to be resolved:
$.when.apply($, promises).done(function() {
// all done here
});
NB: in general it's good practise for an AJAX call to produce a non 2xx return code for errors, rather than an error code within a "successful" HTTP call. This then allows the client side code to use normal AJAX error processing (i.e. .fail callbacks) instead of checking for "errors" within AJAX success processing.
Not sure if my question is subjective/objective but as a JavaScript newbie i'm encountering this problem quite a lot. So here I go.
I'm used to write C#, so my JavaScript structure looks like C#. And just that, that gives problems I think ;-)
Let's give a simple example where I met my problem again today:
MyLibrary.fn.InitAddEntityForm = function () {
$('a#btnAddEntity').click(function () {
//post data and receive object with guid and isPersisted boolean
var persistedObject = MyLibrary.fn.CheckAndSendAddEntityForm("name", "avatarurl.png");
console.log("test");
//check if persisted and go to next step
if (persistedObject.isPersisted) {
MyLibrary.fn.InitAddAnotherEntityForm(persistedObject.gdEntityId);
} else {
alert("Oops, something went wrong. Please call 911");
}
});
};
//////*****/////
//SOME FUNCTION THAT SENDS MY FORM AND RETURNS AN OBJECT WITH TRUE VALUE AND POSTED ENTITY ID
/////*****//////
MyLibrary.fn.CheckAndSendAddForm = function (txtName, ImageUrl) {
var postUrl = "/admin/add";
var persistedObject = new Object();
$.post(
postUrl,
{ Name: txtName, ImageUrl: txtImageUrl},
function (data) {
if (data.Status == 200) {
console.log("Post status:" + data.Message);
persistedObject.isPersisted = true;
persistedObject.gdEntityId = data.Data;
} else if (data.Status == 500) {
console.log("Failed to post entitiy");
} else {
console.log("Fault with Javascript");
}
}, "json"
);
return persistedObject;
};
Okay, thats it. Everything looks okay right? Browser says no.
I tried to debug it using firebug, looping over my code line by line, and that way the browser does what I want: Execute a new function to show the next panel in my wizard.
After placing a lot of Console.logs() in my code I figured out that this must be something about timing in JavaScript. In C# the code executes line by line, but apparently JavaScript doesn't.
By placing that Console.log("test") I noticed that "test" appeared in my console before "Post status: Success!".
So here's my question, how should I write my JavaScript code so I have control over the way the browser executes my code?
Should I really replace the code below to the end of my CheckAndSendAddEntityForm()?
//check if persisted and go to next step
if (persistedObject.isPersisted) {
MyLibrary.fn.InitAddAnotherEntityForm(persistedObject.gdEntityId);
} else {
alert("fout");
}
Is this how I have to write JavaScript: One big domino effect or am I just doing something wrong?
$.post is a shortcut for an AJAX call, AJAX is by definition asynchronous, which means it won't wait on a response before continuing processing. If you switch it to a regular AJAX() method, there is an async option you can set to false, which will make it behave as you are expecting.
Alternatively you can also define a function to execute on successful return of the AJAX request, in which you can call the next step in your process chain.
The AJAX call is asychronous; that means that the callback method exposes by $.post will be executed when the request completes, but your javascript will continue executing as soon as the invoke to $.post finishes. If you want to do something after the ajax call is done, you need to provide a callback method and do something else, ex:
MyLibrary.fn.CheckAndSendAddForm = function (txtName, ImageUrl, callback) {
var postUrl = "/admin/add";
var persistedObject = new Object();
$.post(
postUrl,
{ Name: txtName, ImageUrl: txtImageUrl},
function (data) {
if (data.Status == 200) {
console.log("Post status:" + data.Message);
persistedObject.isPersisted = true;
persistedObject.gdEntityId = data.Data;
} else if (data.Status == 500) {
console.log("Failed to post entitiy");
} else {
console.log("Fault with Javascript");
}
callback(); // This is where you return flow to your caller
}, "json"
);
};
Then you invoke like so:
var persistedObject = MyLibrary.fn.CheckAndSendAddEntityForm("name", "avatarurl.png", function()
{
console.log("test");
//check if persisted and go to next step
if (persistedObject.isPersisted) {
MyLibrary.fn.InitAddAnotherEntityForm(persistedObject .gdPronoId);
} else {
alert("Oops, something went wrong. Please call 911");
}
});
JavaScript is single-threaded. If you have asynchronous functionality, a simple boolean semaphore variable will help not to allow invocations of a function while some processes are running.
If you want to execute asynchronous tasks one by one (like a domino line), you will need to use callback functions.
What you're encountering is the "asynchronous" bit of AJAX. If you want to physically (as in the line line by line in the Javascript file) you can use the .success,.pipe or .done jQuery methods to add a callback to process the data further. Don't embed your callbacks if you can help it, or you will get a "domino effect" as you call it.