Await to AJAX call doen't work as expected - ajax

I have the following two functions:
function scan_request(address, file_url) {
$.ajax({
type: "POST",
async: true,
crossDomain: true,
url: 'http://some_site/api/file/scan',
dataType: "text",
success: function (data, textStatus, jqXHR) {
var json = $.parseJSON(data);//get json response and parse it
$(json).each(function (i, val) {//extract data from json
$.each(val, async function (key, value) {
if (key.toLowerCase() == "jobid") {
var result = await query_request();
alert("result:" + result);
}
});
});
}
});
}
async function query_request() {
var settings = {
"async": true,
"crossDomain": true,
"url": 'http://some_site/api/file/query',
"method": "POST"
}
var res;
$.ajax(settings).then(function (response) {
alert("response: " + response);
res = response;
});
return res;
}
It first alerts result: undefined
and after it alerts: response: [object Object]
But I'm expecting for:
First alert response: [object Object]
And after alert result: [object Object]
It seems like it doesnt wait to the call: var result = await query_request(); and therefore the result is undefined and the alert appears before the inner alert, what am I missing?

You can only await a promise. (Or a function that returns a promise.)
Your query_request() should return the promise that is created by $.ajax(). And since it does not need to await anything itself, it does not need to be marked as async.
// returns a promise, i.e. can be awaited in caller
function query_request(value) {
return $.ajax({
crossDomain: true,
url: 'http://some_site/api/file/query',
method: "POST",
data: {jobid: value}
});
}
Now you can await the result of query_request() inside an async function:
$(json).each(function (i, val) {
$.each(val, async function (key, value) {
if (key.toLowerCase() == "jobid") {
var result = await query_request(value);
alert("result:" + result);
}
});
});
However, this code has a problem - it dasiy-chains the requests inside the loop, when they all could actually be running in parallel. This means it's slower than it needs to be.
Shifting the approach a bit, we can make sure that the Ajax requests are running in parallel instead of one after another:
async function (data, textStatus, jqXHR) {
var todo = [], pending, results;
// make a list of all the things we want to request
$(json).each(async function (i, val) {
$.each(val, function (key, value) {
if (key.toLowerCase() == "jobid") todo.push(value);
});
});
// request them all in parallel (=> array of promises)
pending = todo.map(query_request);
// wait for all of the results
results = await Promise.all(pending)
// ...now work with the results
}

Related

How do I return the response from an ajax call to store in a variable?

I would like to store a response result from an ajax call. This is because the ajax is the main API call used by several functions to extract information from an API.
I call callAPI function more than 8 times in my app.
Of course, I can duplicate the function callAPI 8 times to properly get information but this is not cool way to code.
var result = callAPI("GET",url,'');
$('#status').val(result.success);
$('#output').val(result);
function callAPI(method_input, url_input, body_input){
var urli = url_input;
var datai = body_input;
var method = method_input;
$.ajax({
url: urli,
beforeSend: function(xhrObj){
xhrObj.setRequestHeader("some header","some value");
},
type: method,
data: datai,
})
.done(function(data,status) {
console.log("success");
console.log(data);
return JSON.stringify(data);
})
.fail(function(data,status) {
console.log("error");
console.log(data);
return JSON.stringify(data);
});
}
I tried to store the return value using
var result = ajax(value);
but the result is empty
is there any way to store return value of a function to a variable?
EDIT
I Solved this problem by using callback function like below
function callbackResult(result){
$('#status').val(result.success);
$('#output').val(result);
}
function callAPI(method_input, url_input, body_input, callback){
var urli = url_input;
var datai = body_input;
var method = method_input;
$.ajax({
url: urli,
beforeSend: function(xhrObj){
xhrObj.setRequestHeader("some header","some value");
},
type: method,
data: datai,
})
.done(function(data,status) {
console.log("success");
console.log(data);
return JSON.stringify(data);
callback(data);
})
.fail(function(data,status) {
console.log("error");
console.log(data);
return JSON.stringify(data);
callback(data);
});
}
This was my first function to use a callback function and now I know what the callback function is.
Thank you guys all.
You need 'async': false, so:
var result = $.ajax({
url: "https://api.github.com/users",
'async': false,
type: 'GET'
})
.done(function(data,status) {
console.log("success");
})
.fail(function(data,status) {
console.log("error");
});
console.log("result: " + result.responseText);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
A few things to note:
Instead of JSON.stringify() I think you want to use JSON.parse() to parse the JSON string that is probably been returned by your API.
You can use the $.ajax option dataType to automatically parse the JSON string into an object.
$.ajax() returns a promise which can be chained to add as many callbacks as needed.
A more elegant solution would be to return the promise from your function and chain your callbacks. Ex:
function callAPI(method_input, url_input, body_input) {
var urli = url_input;
var datai = body_input;
var method = method_input;
return $.ajax({
url: urli,
// Automatically parses JSON response
dataType: 'json',
beforeSend: function(xhrObj) {
xhrObj.setRequestHeader("some header", "some value");
},
type: method,
data: datai,
})
.done(function(data, status) {
console.log("success");
console.log(data);
})
.fail(function(data, status) {
console.log("error");
console.log(data);
});
}
callAPI('GET', '').then(function(result){
// Do something with my API result
});
If you plan on making all request at once, with this solution you can consider aggregating all the request into a single promise with $.when(). Ex:
$.when(
callAPI('GET', ''),
callAPI('GET', 'second'),
callAPI('GET', 'third')
).then(function(firstResult, secondResult, thirdResult){
// Do stuff with the result of all three requests
});

Abort chain of AJAX requests with jQuery deferred

I am making a chain of AJAX calls like in the example below, which I found at http://www.dotnetcurry.com/jquery/1022/jquery-ajax-deferred-promises.
How can I adjust the code to prevent that function B gets called if the response from the AJAX call in function A is an empty JSON object array (i.e. "[ ]")? Ideally I would like to not only abort the chain of AJAX calls, but also inform the user that no result was found.
Thanks!
function A() {
writeMessage("Calling Function A");
return $.ajax({
url: "/scripts/S9/1.json",
type: "GET",
dataType: "json"
});
}
function B(resultFromA) {
writeMessage("In Function B. Result From A = " + resultFromA.data);
return $.ajax({
url: "/scripts/S9/2.json",
type: "GET",
dataType: "json"
});
}
function C(resultFromB) {
writeMessage("In Function C. Result From B = " + resultFromB.data);
return $.ajax({
url: "/scripts/S9/3.json",
type: "GET",
dataType: "json"
});
}
function D(resultFromC) {
writeMessage("In Function D. Result From C = " + resultFromC.data);
}
A().then(B).then(C).then(D);
function writeMessage(msg) {
$("#para").append(msg + "<br>");
}
Generically, you could make every one of your worker functions A, B, C, D return an object that contains the response data and whether you want to continue calling the next function in the chain, whatever that may be. Something like this:
function A(input) {
return $.get("/scripts/S9/1.json").then(function (response) {
return {
continue: response.data && response.data.length,
data: response
};
});
}
Now you can make a promise chain by reducing an array of worker functions, and deciding in every step whether you want to continue (in which case you execute the next worker) or not (in which you simply keep returning the last valid result for the rest of the chain). I've wrapped this logic into a function breakableChain, for lack of a better name.
function maybe() { return Math.random() > 0.25; }
function A(input) { console.log('in A:', input.data); return {continue: maybe(), data: 'result from A'}; }
function B(input) { console.log('in B:', input.data); return {continue: maybe(), data: 'result from B'}; }
function C(input) { console.log('in C:', input.data); return {continue: maybe(), data: 'result from C'}; }
function D(input) { console.log('in D:', input.data); return {continue: maybe(), data: 'result from D'}; }
function breakableChain(workers, init) {
return workers.reduce(function (current, next) {
return current.then(function (result) {
return result.continue ? next(result) : result;
});
}, $.Deferred().resolve({continue: true, data: init}));
}
breakableChain([A, B, C, D], 'initial data').then(function (result) {
console.log('overall:', result.data);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Of course you can make a non-generic version of this that doesn't need a continue flag, but instead hard-codes the assumptions about each result directly into the if statement inside .reduce(). That would be shorter, but not re-usable.
The whole {continue: true, data: result} thing is just a convention. Your convention could also be "if the previous call returned anything, then continue, otherwise stop", the approach would be the same, but it would become impossible to return an overall result.

Knockout: Cannot map computed observables after an Ajax call

I have a view model with an Ajax call to save data:
ViewModel = function (data) {
contractsAutocompleteUrl = data.ContractsAutocompleteUrl;
var self = this;
ko.mapping.fromJS(data, lineMapping, self);
self.save = function() {
self.isBeingSaved(true);
$.ajax({
url: data.SaveUrl,
type: "POST",
data: ko.toJSON(self),
contentType: "application/json",
success: function(data) {
if (data.viewModel != null) {
ko.mapping.fromJS(data.viewModel, lineMapping, self);
};
}
});
},
I have some computed variables:
self.TotalSaturdayHrs = ko.pureComputed(function() {
var result = 0;
ko.utils.arrayForEach(self.Lines(),
function(line) {
result = addNumbers(result, line.SaturdayHrs());
});
return result;
}),
self.TotalSundayHrs = ko.pureComputed(function() {
var result = 0;
ko.utils.arrayForEach(self.Lines(),
function(line) {
result = addNumbers(result, line.SundayHrs());
});
return result;
}),
.
.
.
(all the way to Friday)
And a computed GrandTotal:
self.GrandTotalHrs = ko.pureComputed(function() {
var result = addNumbers(0, self.TotalSaturdayHrs());
result = addNumbers(result, self.TotalSundayHrs());
result = addNumbers(result, self.TotalMondayHrs());
result = addNumbers(result, self.TotalTuesdayHrs());
result = addNumbers(result, self.TotalWednesdayHrs());
result = addNumbers(result, self.TotalThursdayHrs());
result = addNumbers(result, self.TotalFridayHrs());
return result;
}),
Now after the Ajax call, the computed observables TotalSaturdayHrs are no longer computed observables, they are simply properties and so my GrandTotal calculation throws an exception.
Why is that and how do I fix this?
What your .save() function should look like (I have a hunch that this will solve your issue):
ViewModel = function (data) {
var self = this,
contractsAutocompleteUrl = data.ContractsAutocompleteUrl;
self.isBeingSaved = ko.observable(false);
self.Lines = ko.observableArray();
ko.mapping.fromJS(data, lineMapping, self);
self.save = function() {
self.isBeingSaved(true);
return $.ajax({
url: data.SaveUrl,
type: "POST",
data: ko.mapping.toJSON(self), // !!!
contentType: "application/json"
}).done(function (data) {
if (!data.viewModel) return;
ko.mapping.fromJS(data.viewModel, lineMapping, self);
}).fail(function (jqXhr, status, error) {
// error handling
}).always(function () {
self.isBeingSaved(false);
});
};
}
ko.mapping.toJSON() will only turn those properties to JSON that also went into the original mapping. ko.toJSON() in the other hand converts all properties, even the calculated ones like TotalSundayHrs.
My wild guess would be that the server returns the same JSON object it had received in the POST, complete with all the ought-to-be-calculated properties like TotalSundayHrs - which then messes up the mapping in your response handler.

HTML.ActionLink redirect not stopping with return false in $.ajax()

i've a HTML.ActionLink on view. what i'm doing is i'm making call to $.ajax() function which checks for return true or false from an anction. it hitting the action, returning the desired result true/false. but the problem is when it returns false. i need to show an alert and redirect should be only in case if its return true..
ActionLink:
<%: Html.ActionLink("Add Race", "AddRace",
new {eventId = Model.EventId, fleetId=Model.SelectedFleet.ID},
new{onclick="return checkFleetAddedandScroing()"}) %>
Function:
function checkFleetAddedandScroing() {
debugger;
$.ajax({
type: "GET",
url: '<%=Url.Action("CheckFleetExists", new {eventId=Model.EventId})%>',
dataType: "json",
cache: false,
success: function (data, textStatus) {
data = eval("(" + data + ")");
if (data == true) {
alert('Ok button clicked');
return true;
}
else {
alert("Cannot delete this fleet becasue either you have already added races to this event or the fleet has used for boat registration.");
return false;
}
}, //success
error: function (req) {
}
});
}
it redirects always..whether it returns true/false..it should redirect only if it returns true....
Please correct me where i'm doing wrong..
You're returning false from the AJAX callback.
That has nothing to do with the return value from the outer function; the AJAX callback won't even start running until later.
you must wait for your request to receive the result, and for doing this set async parameter of ajax function to false.
EDIT: you are lucky with your scenario. you can always return false and in case of successful delete call a function named DoRedirect.
this is the way to go :
function checkFleetAddedandScroing() {
debugger;
$.ajax({
type: "GET",
url: '<%=Url.Action("CheckFleetExists", new {eventId=Model.EventId})%>',
dataType: "json",
timeout: 30000,
cache: false,
success: function (data, textStatus) {
data = eval("(" + data + ")");
if (data == true) {
alert('Ok button clicked');
DoRedirect();
}
else {
alert("Cannot delete this fleet becasue either you have already added races to this event or the fleet has used for boat registration.");
}
}, //success
error: function (req) {
}
});
return false;
}
function DoRedirect(){
//code for do redirect
}
cheers!

Jquery ajax request with async:true does not return value

With async:set to false, the function is returning the correct value. However it blocks the browser. I've tried to add callbacks but it not returning the correct value!
function find_route(array_bustops){
var myresults;
$.ajax({
type: 'POST',
async: true, //with async:false it works
url: 'find_routenum.php',
data:{
array_bustops:JSON.stringify(array_bustops)
},
dataType:'json', //html,xml
success: function(my_results){
myresults=my_results;
},
error:function(x,e){
if(x.status==0){
alert('You are offline!!\n Please Check Your Network.');
}else if(x.status==404){
alert('Requested URL not found.');
}
});
return myresults;
}
WITH CALLBACK:
function find_route(array_bustops,callback){
var myresults;
$.ajax({
type: 'POST',
async: true, //with async:false it works
url: 'find_routenum.php',
data:{
array_bustops:JSON.stringify(array_bustops)
},
dataType:'json', //html,xml
success: function(my_results){
callback(array_bustops,my_results)
},
error:function(x,e){
if(x.status==0){
alert('You are offline!!\n Please Check Your Network.');
}else if(x.status==404){
alert('Requested URL not found.');
}
});
return myresults;
}
function find_route2(myresults){
return myresults;
}
And then i call the function as follows:
arr_one=find_route(arr,find_route2)
but arr_one returns undefined.
EDIT: it still is not working with async:set to true
arr_one=find_route(arr,find_route2)
this got nothing to do with async but with the architecture of your code.
you declare myresults in the scope of find_route, when you call find_route the function return the value of myresults who is undefined because you delcare it only.
Try to declare var myresults='somevalue'; in your function
Now your function return 'somevalue'
This happen because the call to ajax method will not stop the function execution and the success method will be executed way after find_route is called and returned just put a console.log in there you'll see that it will get log eventually when the success is called.
It work when async false is set because async false as you discover stop the function execution from returning before the ajax call is back
Edit: possible solution
function DataListener(val){
this.val = val;
this.remoteFetch();
}
DataListener.prototype.set = function(val){
this.val = val;
this.notifyListener(val)
}
DataListener.prototype.get = function(val){
return this.val;
}
DataListener.prototype.remoteFetch = function(){
$.ajax({
success:$.proxy(this, 'set')
})
}
var arr_one = new DataListener('somevalue');
alert(arr_one.get());// alert somevalue
function doSomething(arr_one_value){
alert(arr_one_value); // the new value set from the ajax call
}
arr_one.addListener(doSomething);
At a glance, you are passing two arguments to your callback, but in your actual callback function you only accept one.
callback(array_bustops,my_results);
// your callback function
function find_route2(myresults){
return myresults;
}
Try passing just the results to the argument.
callback(my_results);
function find_route(array_bustops){
$.ajax({
type: 'POST',
async: true, //with async:false it works
url: 'find_routenum.php',
data:{
array_bustops:JSON.stringify(array_bustops)
},
dataType:'json', //html,xml
success: function(my_results){
// HOW ABOUT PUTTING THE NEXT DESIRED FUNCTIONALITY HERE? //
return my_results
},
error:function(x,e) {
if(x.status==0) {
alert('You are offline!!\n Please Check Your Network.');
}else if(x.status==404) {
alert('Requested URL not found.');
}
//AND THE DUMMY RETURN VALUE OF THE FUNCTION IS HERE//
return FALSE;
}
});
return myresults;
}

Resources