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.
Related
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
}
//From other function
var check = check_abc(a,b,c);
alert (check);
function check_abc(a,b,c)
{
$.ajax({
type: "POST",
url: "abc.php",
data: { a: a,
b: b,
c: c
},
success:function(data) { //alert to get the data and make sure it was return ok.
if(data == "OK")
{
window.alert("YEA");
goOn();
}
else
{
window.alert("Testing");
noGood();
}
}
});
}
Event though it return "OK" (use alert to check before), but no matter what it never show alert("YEA"), why is it so? The data dont seem to be able to compare
That's because you're (correctly) using an asynchronous AJAX call. When you call check_abc(a,b,c), it immediately returns an undefined value, since the response from the server will come later.
Simplify your calling code to this:
//From other function
check_abc(a,b,c);
And inside the success function itself, display the dialog (which will happen when you get a response from the server):
success:function(data) { //alert to get the data and make sure it was return ok.
var displayBoolean = date === "OK";
alert(displayBoolean);
}
AJAX is asynchronous that why you can't get value
try like this
success:function(data) { //alert to get the data and make sure it was return ok.
if(data == "OK")
{catchVal("OK");return true; } //tried check == true; still cannot
else
{catchVal("Error");return false;}
}
function catchVal(val){
alert(val);
}
$.ajax({
type: "POST",
dataType: "text",
url: "abc.php",
data: { a: a,
b: b,
c: c
},
success:function(data) {
var result = $.trim(data);
if(result == "OK")
{
window.alert("YEA");
goOn();
}
else
{
window.alert("Testing");
noGood();
}
I have a function with Ajax call. If it returns true, another function with another Ajax should be run. But in my case first function returns true but the second function doesn't run. How to do that?
If I remove this: if(checkIfCompleted()) { from the second function, it's working. But I have to check if first function returns true.
My code is:
function checkIfCompleted() {
$.ajax(
{
url: Base_URL + "/offers/" + 'checkIfQuoteIsComplete',
type: "POST",
data: {
"offer_id": offer_id
},
success: function (data) {
if(data === 'notcompleted') {
$("#NotCompleteQuotes").modal();
return false;
} else {
return true;
}
},
error: function (data) {
MessageBoxError('Error');
}
});
}
Second function that should run after first function returns true is:
function saveOrEditQuote(status, id) {
if(checkIfCompleted()) {
//my code here - it's long
return false;
}
}
Javascript is asynchronous, try using callbacks like this
function checkIfCompleted(callback) {
$.ajax(
{
url: Base_URL + "/offers/" + 'checkIfQuoteIsComplete',
type: "POST",
data: {
"offer_id": offer_id
},
success: function (data) {
if(data === 'notcompleted') {
$("#NotCompleteQuotes").modal();
} else {
callback();
}
},
error: function (data) {
MessageBoxError('Error');
}
});
}
then
function saveOrEditQuote(status, id) {
checkIfCompleted(function(){
// will be done only on success
//my code here - it's long
return false;
})
}
Explanation:
A callback is simply another argument, the difference is that it is a function, not for example an integer. By using them you can make sure some block of code is called only after something. They're really helpful for actions that take some time (like requests, jquery animations, timeouts).
All you have to do is pass in the second callback function saveOrEditQuote() into the body of the success callback. You'll need be careful to declare that function in the right scope, because the success callback will be a different scope than checkIfCompleted.
Also, often the success argument data will be an array of objects and strings so data === 'notcompleted' may be comparing an array to a string.
function checkIfCompleted (){
var success = saveOrEditQuote;
$.ajax({ ... }).success(function(data){ success(true); })
}
function saveOrEditQuote(isCompleted){...};
How can I make second AJAX request in the function below asynchronous instead of synchronous? result is a string that should start with 'start of string' and end with 'end of string' but in the middle of the string will be the results of an initial AJAX request that is being iterated.
Foo = {
foo: function() {
$(document).on("change", '.foo', function(e) {
e.preventDefault();
$.ajax({
url: "foo.php",
success: function(rows) {
$.each(rows, function() {
var result = 'start of string'; // START
$.ajax({
url: "bar",
async: false, // I DON'T want this
success: function(data) {
result += data; // MIDDLE
}
});
result += 'end of string'; // END
});
}
});
});
}
}
Thank you.
You can take advantage of jquery deferred objects. You can have two different ajax calls in different functions and then you can use .done() method to make sure you get the final string once both the ajax calls have been completed.
Read more here:
http://api.jquery.com/category/deferred-object/
http://api.jquery.com/deferred.done/
In jquery the way to chain asynchronous calls which are executed one after other, is using promise.then(), which was also called promise.pipe() in previous jquery versions.
Foo = {
foo: function() {
$(document).on("change", '.foo', function(e) {
e.preventDefault();
var param1 = {url: "foo.php"};
var param1 = {url: "bar"};
$.ajax(param1)
.then(function(rows) {
var result = 'start of string'; //START
var fn = function(data) {
result += data;
}
var last, first;
$.each(rows, function() { // rows should be iterable
if (!last) {
first = last = $.ajax(param2).done(fn)
} else {
last = last.then(function(res) {
return $.ajax(param2).done(fn);
});
}
});
last.done(fn).done(function() {
result += 'end of string'; // END
});
return first;
});
});
}
}
The easiest way to do this is indeed by using deferred objects, the example of your JavaScript using the jQuery when and done deferred statements;
$.when(ajaxCallOne()).done(ajaxCallTwo(rows));
function ajaxCallOne() {
return $.ajax({
url : "foo.php",
async: true
});
}
function ajaxCallTwo(rows) {
var result = 'start of string';
$.each(rows, function() {
$.ajax({
url : "bar",
async : true,
success : function(data) {
result += data; // MIDDLE
}
});
result += 'end of string'; // END
});
return result;
}
I'm not a JavaScript nor jQuery expert, but I think you should look at those deferred objects.
http://api.jquery.com/category/deferred-object/
http://api.jquery.com/jQuery.when/
I think you should look at the javascript promise design pattern
let me explain little to you:
This promise is a sort of proxy, representing the future result of the
operation. You would then register a callback on the promise, which
will be executed by the promise once the operation does complete and
the result is available.
Using jQuery you can define promise like this:
var promise = $.getJSON('url');
then using your promise variable you can define what to do when your request is done or failed or even do some function in case it failed or succeded.
promise.done(function(s) {alert('done successfully'); });
promise.fail(function(){ alert('get failed!'); });
promise.always(function(){ alert('this 'll executed anyway!'); });
there are plenty of tutorials on it
Promises and Deferred objects in jQuery
jQuery: Deferred Object
javascript promises
What is the benefit of a 'promise' abstraction in CommonJS?
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!