we are trying to change some calls in the application from sync to async then i read the post
"Call An Asynchronous Javascript Function Synchronously"
so when i call the callThis() I am getting the output as:
"Success"
"failure"
but I need the output as
"Success"
"KATE success"
Can you guys suggest me a solution for this, not the callback or timeout but still an efficient technique when changed from sync to async
function callThis()
{
if(kate())
{
console.log("KATE success")
}
else
{
console.log("failure")
}
}
function kate()
{
$.ajax({
url: "www.google.com" (http://www.google.com),
type: 'get',
async: true,
timeout: 10000,
success: function (data) {
console.log("Success");
},
error: function () {
console.log("FAIL!!!");
}
});
}
The solution isn't to call it synchronously, but to work with the asynchronous nature of ajax
function callThis() {
kate().done(function(result) {
if ( result ) {
console.log("KATE success")
} else {
console.log("failure")
}
}).fail(function(error) {
console.log(error);
});
}
function kate() {
return $.ajax({
url: "www.google.com",
type: 'get',
async: true,
timeout: 10000
});
}
note that getting google.com will fail due to the same-origin policy
You could use the promise interface that jQuery returns (.done), like this:
function callThis() {
kate().done(function(result) {
if ( result ) {
console.log("KATE success")
} else {
console.log("failure")
}
}).fail(function(error) {
console.log(error);
});
console.log("KATE SPADE")
}
function kate() {
return $.ajax({
url: "www.google.com",
type: 'get',
async: true,
timeout: 10000
});
}
Even the asynchronous nature of ajax is now taken into account, I am still getting the output as:
KATE SPADE \n
KATE success
Related
I have an ASP.NET MVC app and I am trying to make a chain of operations, one after another using jQuery inside a javascript function. The function consists of three parts.
What I am trying to do is: If some condition is satisfied then I want to execute syncrhonous jQuery ajax call CheckData. Dependending on the result returned:
It returns ok -> I want to continue executing part2 and finally part3 in this order.
It returns Nok -> then it finishes and returns. It does not continue executing part2 and part3.
So I have set async: false but it is not working, program continues executing part2 and part3.
I know async:false is deprecated so how can I achieve this?
function onCheckValidData()
{
// do something....
// PART 1 STARTS HERE
if (some_condition_is_satified)
{
$.ajax({
url: '#Url.Action("CheckData", "MyController")',
async: false,
type: "POST",
dataType: "JSON",
beforeSend: function () {
showLoading();
},
success: function (result) {
if (!result.isOk) {
return;
}
},
complete: function(){
hideLoading();
}
});
}
// PART 2 STARTS HERE
// do something.....
// continue doing more thing.....
// more things.....
// PART 3 STARTS HERE
$.ajax({
url: '#Url.Action("MyActionMethod1", "MyController")?' + paramsStr,
type: "POST",
dataType: "html",
beforeSend: function () {
showLoading();
},
success: function (result) {
if (result == 'True') {
jsMethod2(); // jsMethod2 is another javascript method which contains another $.ajax block
}
else if (result == 'False') {
jsMethod3(); // jsMethod3 is another javascript method which contains another $.ajax block
}
else {
alert(result);
}
},
complete: function(){
hideLoading();
}
});
}
My actions in the controller:
private JsonResult CheckData()
{
MyBoolResult res = new MyBoolResult();
// do something....
return Json(new { isOk = res.isOk });
}
public String MyActionMethod1(String param1, String param2, bool param3, string param4, string param5)
{
// do something
return condition ? "True" : "False";
}
No need to make that synchornuous. If u want the "PART 2" and "PART 3" to wait for the ajax-request to finish just put them into a function and call them on success:
function onCheckValidData()
{
// do something....
// PART 1 STARTS HERE
if (some_condition_is_satified)
{
$.ajax({
url: '#Url.Action("CheckData", "MyController")',
type: "POST",
dataType: "JSON",
beforeSend: function () {
showLoading();
},
//Success will execute only if the ajax-request is finised
success: function (result) {
if (!result.isOk) {
return;
}
part2();
part3();
},
complete: function(){
hideLoading();
}
});
}
// PART 2 STARTS HERE
function part2 () {/*do something.....*/}
// PART 3 STARTS HERE
function part3 () {/*$.ajax({...})*/}
}
I am doing a few recurring AJAX calls where I pass an array from the front-end to the back-end and whenever it comes back to the front-end, the array gets smaller (by 1) and ultimately it'll be empty, therefore my recursive calls will stop.
Here's my calls:
function download_required_files(demo_data) {
var ajaxsecurity = setup_page_params.ajax_nonce;
jQuery.ajax({
url: ajaxurl,
type: 'POST',
dataType: 'json',
data: {
action: 'download_import_files_request',
security: ajaxsecurity,
content_install_request_data: JSON.stringify(demo_data),
},
success: function (response) {
console.log(response);
var data = response.data || false;
/**
* If no steps are left, meaning that all required files have been downloaded, proceed with the whole install process.
*/
if(!data.remaining_steps || !data.remaining_steps.length) {
return false;
}
if(data.can_continue !== 'yes') {
return false;
}
if(data.remaining_steps && data.remaining_steps.length) {
demo_data.steps_to_take = data.remaining_steps;
download_required_files(demo_data);
}
$('.demo-loader-content').fadeOut();
},
error: function (response) {
$('.demo-loader-content').fadeOut();
}
});
}
Assuming I have 2 steps to download files for, this download_required_files will run twice, then it'll be done, but if I do:
var download_process = download_required_files(demo_data) //Runs 2 times
download_process.done(function() { //Do stuff here once that function ran 2 times });
It gives me the: Cannot read property 'done' of undefined error and for good reason. That download_process is not a promise object for it to have that property, it's just...empty.
Where should I intervene in my download_required_files so that it signals to outside code that "Hey, in a promise environment, I'm done!"?
Although the result of the call to $.ajax is a jqXHR object, which is promise-like, for what you describe I think I'd go with your own native Promise (or Deferred if you prefer) to represent the overall recursive process:
function download_required_files(demo_data) {
return new Promise(function(resolve, reject) {
function worker() {
var ajaxsecurity = setup_page_params.ajax_nonce;
jQuery.ajax({
url: ajaxurl,
type: 'POST',
dataType: 'json',
data: {
action: 'download_import_files_request',
security: ajaxsecurity,
content_install_request_data: JSON.stringify(demo_data),
},
success: function (response) {
console.log(response);
var data = response.data || false;
/**
* If no steps are left, meaning that all required files have been downloaded, proceed with the whole install process.
*/
if(!data.remaining_steps || !data.remaining_steps.length) {
// *** All done
$('.demo-loader-content').fadeOut();
resolve();
} else if(data.can_continue !== 'yes') {
// *** All done; but is this an error condition? If so
// use `reject` instead of `resolve` below.
$('.demo-loader-content').fadeOut();
resolve();
} else {
demo_data.steps_to_take = data.remaining_steps;
worker(); // This is the internal recursive call
}
},
error: function (response) {
$('.demo-loader-content').fadeOut();
}
});
}
worker();
});
}
Or using Deferred instead:
function download_required_files(demo_data) {
var d = $.Deferred();
function worker() {
var ajaxsecurity = setup_page_params.ajax_nonce;
jQuery.ajax({
url: ajaxurl,
type: 'POST',
dataType: 'json',
data: {
action: 'download_import_files_request',
security: ajaxsecurity,
content_install_request_data: JSON.stringify(demo_data),
},
success: function (response) {
console.log(response);
var data = response.data || false;
/**
* If no steps are left, meaning that all required files have been downloaded, proceed with the whole install process.
*/
if(!data.remaining_steps || !data.remaining_steps.length) {
// *** All done
$('.demo-loader-content').fadeOut();
d.resolve();
} else if(data.can_continue !== 'yes') {
// *** All done; but is this an error condition? If so
// use `d.reject` instead of `d.resolve` below.
$('.demo-loader-content').fadeOut();
d.resolve();
} else {
demo_data.steps_to_take = data.remaining_steps;
worker(); // This is the internal recursive call
}
},
error: function (response) {
$('.demo-loader-content').fadeOut();
}
});
}
worker();
return d.promise();
}
This would be my approach, separating the individual AJAX requests from the looping over the content, and that also from the DOM updates:
function download_one_file(demo_data) {
return jQuery.ajax({
url: ajaxurl,
type: 'POST',
dataType: 'json',
data: {
action: 'download_import_files_request',
security: setup_page_params.ajax_nonce,
content_install_request_data: JSON.stringify(demo_data),
}
});
}
function download_loop(demo_data) {
return download_one_file(demo_data).then(function(data) {
if (!data) {
return Promise.reject();
} else if (data.remaining_steps && data.remaining_steps.length) {
demo_data.steps_to_take = data.remaining_steps;
return download_loop(demo_data);
} else {
return Promise.resolve();
}
});
}
function download_required_files(demo_data) {
return download_loop(demo_data).finally(function() {
$('.demo-loader-content').fadeOut();
});
}
I wrote this object and I run it into the page I have:
var dataPage = {
getData: function() {
return $.ajax({
url: '/my_url/',
data: {
product: 'some_product',
state: 'some_state'
},
type: 'POST',
async: true,
success: function (data) {
//console.log(data);
dataPage.results = data;
},
error: function (xhr, ajaxOptions, thrownError) {
alert('Error:' + xhr.status);
alert(thrownError);
}
});
}
,returnData: function(){
var xhr = this.getData();
//console.log(xhr);
xhr.done(function() {
//console.log(xhr.responseText);
this.results = xhr.responseText;
$('#JSON').html(this.results);
});
}
}
var results = dataPage.returnData()
console.log(results)
It works perfectly as the attribute async set to false: the data is loaded into the div with id="JSON". The two console.log()s return the data and everything works fine.
Now I would like to switch to async: true but I don't know how to apply closure to make the function pass the resulting data correctly, avoiding the xhr.responseText to be undefined because of the asynchronous nature of the getData() call.
EDITED
I edited the code above, added the returnData() function, but the last console.log() still return undefined. Adding the .done() didn't solve the problem of taking out to the global scope the results if async: true...
Use the done() callback:
var jqXHR = dataPage.getData();
jqXHR.done(function(result) {
$('#JSON').html(result);
});
For some reason my function is returning undefined while seemingly working in itself.
function getDomains() {
$.ajax({
url: '/rewrites/cgi-bin/ajax.pl?action=listdomains',
dataType:'json',
async: false,
success: function( data ) {
if (data.error) {
alert(data.error);
}
else {
alert(data.domains);
return(data.domains);
}
}
});
}
alert(getDomains());
My first alert shows a populated list but the second is undefined. Does this make any sense?
You're in a function for the success call. That's why you're not getting a result from your getDomains function. Assign it to a variable and return the variable after the ajax call.
function getDomains() {
var results;
$.ajax({
url: '/rewrites/cgi-bin/ajax.pl?action=listdomains',
dataType:'json',
async: false,
success: function( data ) {
if (data.error) {
alert(data.error);
}
else {
alert(data.domains);
results = data.domains;
}
}
});
return results;
}
alert(getDomains());
Why don't you just do this, assuming you need your return for a function called whateverFunc():
function getDomains() {
$.ajax({
url: '/rewrites/cgi-bin/ajax.pl?action=listdomains',
dataType:'json',
async: false,
success: function( data ) {
if (data.error) {
alert(data.error);
}
else {
whateverFunc(data.domains);
}
}
});
}
function whateverFunc(domains){
alert(domains);
}
You can't return anything from success callback, it makes no sense.
I would like also to complain about the async:false here. Why do you need it absolutely?
You should let the call be async and manage the blocked state by yourself with some mutex or something around. In fact, you should trigger popup or whatever you need to do after you get the answer in the whateverFunc().
It's clean and you keep control on the blocking state.
in my MVC layout page I have the following:
$("body").ajaxError(
function (e, request) {
if (request.status == 403 || request.status == 500) {
window.location = '#Url.Action("LogOn", "Account", new {area = "", msg = "forbidden", returnUrl = HttpContext.Current.Request.RawUrl})' + window.location.hash;
return;
}
window.location = '#Url.Action("Index", "Error")';
}
);
on another page I'm performing an ajax call like so:
...
$.when(refreshActionLinks(row, machineId, packageId)).done(function(a1) {
row.find("span").text(opStatus).removeClass("pending");
progressbar.progressbar("destroy");
$(row).flash(bg[1], 1000);
});
...
javascript function:
function refreshActionLinks($row, machineId, packageId) {
try {
var json = JSON.stringify({ packageId: packageId, machineId: machineId, tabType: $("#TabType").val() });
console.log("refreshActionLinks => " + json);
$row.find("td.options div.actionLinks").html("<img src='#Url.Content("~/Content/images/ajax-load2.gif")' />"); // pending
return $.ajax({
url: "#Url.Action("GetActionLinks", "Packages")",
data: json,
timeout: 50000,
contentType: 'application/json',
type: 'POST',
success: function (data) {
if ($row.length) {
$row.find("td.options div.actionLinks").html(data);
}
},
error: function(jqXHR, textStatus, errorThrown) {
console.log(textStatus, errorThrown);
}
});
} catch(e) {
// hide icons
$row.find("a.action").remove();
}
}
The issue is that while refreshAction function is executing, clicking a menu link causes the ajax call to error out - which in this case is correct. BUT it does take me to /Index/Error page which is NOT correct. I would like "$("body").ajaxError" to handle all ajax errors on the site EXCEPT on the page I'm calling refreshActionLinks. Notice, I already have try/catch surrounding my ajax call. why doesn't that work?
thanks
figured it out:
ajax has a settings:
global: false
now my function looks like this:
function refreshActionLinks($row, machineId, packageId) {
try {
var json = JSON.stringify({ packageId: packageId, machineId: machineId, tabType: $("#TabType").val() });
console.log("refreshActionLinks => " + json);
$row.find("td.options div.actionLinks").html("<img src='#Url.Content("~/Content/images/ajax-load2.gif")' />"); // pending
return $.ajax({
url: "#Url.Action("GetActionLinks", "Packages")",
global: false, // disable error pages on failed ajax calls
data: json,
timeout: 50000,
contentType: 'application/json',
type: 'POST',
success: function (data) {
if ($row.length) {
$row.find("td.options div.actionLinks").html(data);
}
}
});
} catch(e) {
// hide icons
$row.find("a.action").remove();
}
}