How to stop $.ajax within for loop? - javascript

I'm running into an issue where my api call reaches it's timeout limit, but continues to loop for the remainder of the requests provided resulting in n number of timeout logs in the console (In this case 5). I want it so that I can do something along the lines of a break; and just exit entirely so the remaining calls don't get logged. E.g. If the call immediately times out, only one timeout log will be logged instead of the current 5 and none of the five api requests will be made.
let qs = {
requests: 5,
timeout: 1000
};
let prices = [];
let highest = 0;
const url = 'http://somedata.com';
function xhr(qs){
return $.ajax({
url: url,
dataType: 'json',
timeout: qs.timeout,
success: function (data) {
let json = JSON.stringify(data['price']);
prices.push(json);
getHighestPrice(prices);
console.log(highest);
},
error: function(e, textstatus, message) {
if(textstatus==="timeout") {
console.error(textstatus);
} else {
console.error(textstatus);
}
}
});
}
function makeRequest(qs) {
for(let i = 0; i < qs.requests; i++) {
xhr(qs);
}
}
function getHighestPrice(arr) {
for(let i = 0; i <= arr.length; i++) {
if (arr[i] > highest) {
highest = arr[i]
}
}
return highest;
}
makeRequest(qs);

Your code makes all the requests at once
It should be noted that this code will stop the "chaining" once any error occurs in $.ajax, not just timeout - if that's not the required behaviour, there is a little more to do
To make the call only if the previous is successful, you can chain the promises returned by $.ajax
let qs = {
requests: 5,
timeout: 1000
};
let prices = [];
let highest = 0;
function xhr(qs){
return $.ajax({
url: url,
dataType: 'json',
timeout: qs.timeout,
success: function (data) {
let json = JSON.stringify(data['price']);
prices.push(json);
getHighestPrice(prices);
console.log(highest);
},
error: function(e, textstatus, message) {
if (textstatus==="timeout") {
console.error(textstatus);
} else {
console.error(textstatus);
}
}
});
}
function makeRequest(qs) {
let p = $.when();
for(let i = 0; i < qs.requests; i++) {
p = p.then(() => xhr(qs));
}
}
as others have pointed out you don't need to pass qs to xhr, however, I'm assuming the code you posted may be simplified so have not removed the qs argument
An alternative would be
let qs = {
requests: 5,
timeout: 1000
};
let prices = [];
let highest = 0;
function xhr(qs){
return $.ajax({
url: url,
dataType: 'json',
timeout: qs.timeout
}).then(data => {
let json = JSON.stringify(data['price']);
prices.push(json);
getHighestPrice(prices);
console.log(highest);
});
}
function makeRequest(qs) {
let p = $.when([]);
for(let i = 0; i < qs.requests; i++) {
p = p.then(() => xhr(qs));
// or p.then(xhr); if you don't need to pass qs on to xhr function (remove qs argument in xhr as well)
}
p.then(() => {
// this is run once all have completed
}).fail(reason => {
// this is run if there's a failure anywhere
});
}

Since it is a callback, it will be executed asynchronously. So even if you throw an error from one of the callback you provided, the rest will be executed later or sooner. One of the solution I could think of is to have a flag that will be set to true if one of the AJAX causes an error. Something like:
var hasError = false;
$.ajax({
error: function (e, textstatus, message) {
if (textstatus === "timeout") {
if (!hasError) console.error(textstatus);
hasError = true;
}
}
});

Using Promise.all() can simplify this use case. If you cannot use promises try throwing an exception from the error handler. Like so:
$.ajax({
error: function (e, textstatus, message) {
if (textstatus === "timeout") throw e
}
})
Be sure to catch the the exception:
function makeRequest(qs) {
try {
for(let i = 0; i < qs.requests; i++) {
xhr(qs);
}
} catch (e) { // Handle error here }
}

To get the desired behavior you will have to make all of the calls sequentially i.e. you can't start the next call until the previous one has finished (otherwise you won't know if it has failed or not).
You could use the done callback to determine whether the next call should be made:
function makeRequest(i) {
xhr().done(function(){
if (i < qs.requests){
makeRequest(i+1)
}
})
}
makeRequest(0); // Kick things off here
Also, you don't need to pass the qs variable into the makeRequest or xhr functions. It doesn't change throughout the calls so just use it as-is within the xhr function without passing it around.

Related

How to fix jQuery Promise .then() from being called before the AJAX function is executed completely?

I am using Promise to execute my AJAX function, and am using `.then()' when calling the function to imply that anything in the .then() scope is executed once the AJAX function is executed. However, the .then() still executes before the AJAX function is fully done retrieving data from the server. I have been stuck on this for really long, and am unable to figure out what exactly is causing the issue. Is my code wrong? Below is my code. Any help will be deeply appreciated!
Calling the AJAX Function
userLayer = this.addUserLayerTreemail(
$loadingContent,
$loadingDiv,
treeLayers,
map,
heritage_cluster_layer_view_18,
flowering_cluster_layer_view_18,
IMP_FIELDS,
suggestion,
userPlottedLayerView18Options,
userLayer,
$speciesError
).then( function(response){
//this gets executed before the function retrieves all the data from the server - it should return true, but returns false
console.log(map.hasLayer(userLayer), "check if layer exists")
setSearchState(true);
$mapSearch.blur();
}).catch(function(err){
console.log("error caught", err)
});
Defining the AJAX Function
addUserLayerTreemail(
$loadingContent,
$loadingDiv,
treeLayers,
map,
heritage_cluster_layer_view_18,
flowering_cluster_layer_view_18,
IMP_FIELDS,
suggestion,
userPlottedLayerView18Options,
userLayer,
$speciesError
) {
// let checked = this.getCurrentFilter(),
const featureType = 'USERTREE';
let queryString = '';
suggestion = suggestion.toLowerCase();
let $selectedOptionSearch = $('.mapFilterBtns.is-active').data('value');
let all_trees="";
let searchText= $('#mapSearch').val();
let configData = {"searchText": searchText, "searchType" : $selectedOptionSearch};
console.log("config", configData)
// this function is called in the resolve() function below
function retrieveTrees(response) {
let conditionalVal = " or ";
let treeID = "";
for(let i=0; i<response.length; i++) {
treeID = response[i]['MYTreeID']
if(i==0) {
all_trees = `${IMP_FIELDS.TREEMAIL_SEARCH} like '%${treeID}%'`;
} else {
all_trees += conditionalVal + `${IMP_FIELDS.TREEMAIL_SEARCH} like '%${treeID}%'`;
}
}
console.log("all trees here",all_trees);
queryString = all_trees + ` and Actual_Tree_Type like '%${featureType}%'`;
let isSearch = true;
let layerOptions = $.extend(
{
where: queryString
},
userPlottedLayerView18Options
);
layerOptions.url = mapLayers.userPlantedLayer;
userLayer = L.esri.featureLayer(layerOptions);
userLayer
.query()
.where(queryString)
.run((err, featureCollection, response) => {
if (featureCollection.features.length > 0) {
removeClass($loadingDiv, 'show');
//adding the userLayer to the map
userLayer.addTo(map);
//this returns true after the .then() chunk is executed in the call above
console.log(map.hasLayer(userLayer), "checking in ajax")
});
console.log(map.hasLayer(userLayer), "checking in ajax ")
return userLayer;
}
return new Promise(function(resolve, reject){
$.ajax({
url: "../apis/treemailSearch",
method: 'GET',
dataType: 'json',
data : configData,
contentType: 'application/json',
success: response => {
return resolve(retrieveTrees(response));
},
error : function(err){
reject(err, "printing error here");
}
});
})
}

how to prevent calling next iteration loop without getting callback response from webservice

I am calling the webservice in the loop for multple documents one by one.Each transaction will take atleast 4 to 5 secs to complete the transacion.
the problem is even before getting callback response from webservice its going for the next iteration nodeRef document.
how to prevent calling next iteration without getting callback response from webservice ?
for (var i = 0; i < nodeRef.length; i++) {
var config = {
url: "slingshot/doclib/action/checkout/node/" + nodeRef[i],
method: "POST",
successCallback: this.onActionEditOfflineSuccess,
failureCallback: this.onActionEditOfflineFailure,
callbackScope: this
};
this.serviceXhr(config);
}
onActionEditOfflineSuccess: function alfresco_services_ActionService__BulkEditOfflineSuccess(response, originalRequestConfig) {
console.log("success");
}
onActionEditOfflineFailure: function alfresco_services_ActionService__BulkEditOfflineFailure(response, originalRequestConfig) {
console.log("fail");
}
Try this approach:
var index = 0;
onActionEditOfflineSuccess: function
alfresco_services_ActionService__BulkEditOfflineSuccess(response,
originalRequestConfig) {
console.log("success");
callService(index++);
}
onActionEditOfflineFailure: function
alfresco_services_ActionService__BulkEditOfflineFailure(response,
originalRequestConfig) {
console.log("fail");
callService(index++)
}
function callService(index){
if(index < nodeRef.length){
var config = {
url: "slingshot/doclib/action/checkout/node/" + nodeRef[i],
method: "POST",
successCallback: this.onActionEditOfflineSuccess,
failureCallback: this.onActionEditOfflineFailure,
callbackScope: this
};
this.serviceXhr(config);
}
}
callService(index);

Executing a Javascript function only after the previous function is fully executed [duplicate]

This question already has answers here:
Execute a function after two (or more) ajax request succeed
(4 answers)
Closed 3 years ago.
I have 2 functions, doFirst() and doSomethingElse(). Now the first function consists of about 10 AJAX Requests which are executed on a condition which checks if that filter was picked. These AJAX Requests are communicating with a URL and save a set of IDs in the respective arrays. After this is done, the doSomethingElse() has to fire which draws the results table. At this point I am executing them by setting a time out. I would like to know a better way to wait for functions to finish and then execute the next function. Any help is greatly appreciated.
Thanks!
//How I am currently calling the functions:
doFirst();
doSomethingElse();
function doFirst(){
var filterArrayTaxSel1 = $("#taxIA span").get().map(el => el.textContent);
for (var i = 0; i < filterArrayTaxSel1.length; i++) {
filterArrayTaxSel1[i] = filterArrayTaxSel1[i].replace(" ", "%20");
}
// taxgroup2 - Selector
queryBuilder = filterArrayTaxSel1;
//console.log(queryBuilder);
console.log(filterArrayTaxSel1);
if (filterArrayTaxSel1.length > 0) {
if (filterArrayTaxSel1.length > 0 && filterArrayTaxSel1[0] != "All") {
console.log("I am inside the IF!");
for (var i = 0; i < filterArrayTaxSel1.length; i++) {
var baseURL = "some URL here";
console.log(baseURL);
responses(baseURL);
function responses(baseURL) {
$.ajax({
url: baseURL,
type: "get",
cache: false,
headers: {
'Content-Type': 'application/json'
},
success: function (data) {
console.log(data.features.length);
for (var i = 0; i < data.features.length; i++) {
if (taxArrayT1.indexOf(data.features[i].properties.taxon_id) == -1) {
taxArrayT1.push(data.features[i].properties.taxon_id);
}
}
console.log("In the Invertebrate Animals Section 1");
console.log(taxArrayT1.length);
}
})
}
}
}
else if (filterArrayTaxSel1[0] == "All") {
console.log("I am inside the IF!");
var baseURL = "some URL here";
console.log(baseURL);
responses(baseURL);
function responses(baseURL) {
$.ajax({
url: baseURL,
type: "get",
cache: false,
headers: {
'Content-Type': 'application/json'
},
success: function (data) {
console.log("I am inside the ELSE IF ALLL!");
console.log(data.features.length);
for (var i = 0; i < data.features.length; i++) {
if (taxArrayT1.indexOf(data.features[i].properties.taxon_id) == -1) {
taxArrayT1.push(data.features[i].properties.taxon_id);
}
}
console.log("In the Invertebrate Animals Section 2");
console.log(taxArrayT1.length);
}
})
}
}
//Selection 1 Tax Group AJAX Call Sensitivity ARRAY WITH 0 to Multiple CASES - ENDS.
}
//some more AJAX Calls depending on whether the filter exists
//End of function
}
function doSomethingElse(){
//Code to draw the Table using the arrays from the previous function
}
The Promise object represents the eventual completion (or failure) of
an asynchronous operation, and its resulting value.
Here is an example of how you can use Promise object to make your async code (such as AJAX requests) a bit easier to handle. Promise.all seems like it would be a good candidate to help resolve your issue. You could do something like this:
const doFirst = () => {
console.log('Making AJAX requests, please wait..');
const asyncActions = [
//Wrap all your async code in a Promise
new Promise((resolve, reject) => {
//Resolve some phony data as part of our AJAX request simulation
setTimeout(() => resolve({
name: 'Item 1',
id: 1
}), 1000)
}),
new Promise((resolve, reject) => {
setTimeout(() => resolve({
name: 'Item 2',
id: 2
}), 4000)
}),
Promise.resolve({
name: 'Item 3',
id: 3
})
];
//Since all your async actions are stored in a iterable, we can use
return Promise.all(asyncActions);
}
const doSomethingElse = values => {
console.log('Here is the data returned from our AJAX requests:');
values.forEach(value => {
console.log(`Name: ${value.name} || ID: ${value.id}`)
});
}
doFirst().then(doSomethingElse);
Promise is simply an object to which you can pass some function that does some work, and when it's finished it invokes a callback function - which you use for further actions.
You can find full documentation on: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/prototype
As for example, you have a function, that returns Promise object:
function makeAjaxCall(url, methodType){
var promiseObj = new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest();
xhr.open(methodType, url, true);
xhr.send();
xhr.onreadystatechange = function(){
if (xhr.readyState === 4){
if (xhr.status === 200){
console.log("xhr done successfully");
var resp = xhr.responseText;
var respJson = JSON.parse(resp);
resolve(respJson);
} else {
reject(xhr.status);
console.log("xhr failed");
}
} else {
console.log("xhr processing going on");
}
}
console.log("request sent succesfully");
});
return promiseObj;
}
You then make multiple ajax calls:
let promise1 = makeAjaxCall(URL1, "GET");
let promise2 = makeAjaxCall(URL2, "GET");
let promise3 = makeAjaxCall(URL2, "GET");
Then you have a "listener", that waits for all promises (ajax calls) to be finished:
Promise.all([promise1, promise2, promise3]).then(function(values) {
console.log(values);
});
You have many articles and documentation on that subject:
https://medium.com/front-end-weekly/ajax-async-callback-promise-e98f8074ebd7
http://ccoenraets.github.io/es6-tutorial-data/promisify
https://api.jquery.com/jquery.ajax
From your code you are using jQuery so you can utilize some of its built in methods. You want to basically keep track of all your Ajax calls in an array so when you make an Ajax call, push them into an array. You can then use jQuery's $.when() to execute the next step when they are all complete.
Basic idea:
function firstStep() {
var requests = []
var list = [1,2,3]
for (var i = 0; i < list.length; i++) {
requests.push($.ajax(...));
}
return requests
}
function secondStep() {
console.log(arguments)
}
var requests = firstStep()
$.when.apply(undefined, requests).then(secondStep)
If you were not using jQuery, it would be promises with promise.all

How to call a function associated with ajax within the loop in javascript?

I am running a loop & within the loop I am calling a function. The function makes an ajax call & returns response but the problem is loop is not waiting for the response therefore the function returns undefined.
function checkBidStatus(index) {
$.ajax({
type:'get',
url:'/orderbook/checkorderdetails',
success: function(data){
$parsedData = JSON.parse(data);
if($parsedData[index]) {
return "Hello";
} else {
return "Bye";
}
//$("#bid-details .modal-dialog").html(data);
},
error: function (response) {
console.log(response);
}
});
}
let content = '';
for(let i = 1; i <= 10; i++ ) {
content += '<div>' +
(checkBidStatus(i)) +
'</div>';
}
The problem is that the for loop is synchronous, but the ajax call is asynchronous. I.e., that function has to wait for the server to respond. Therefore, callbacks or promises need to be used by the caller before iterating to the next request.
There are elegant ways to accomplish this task using certain libraries, such as async, but the "quick vanillaJS" way to solve this task using promises would be something like this:
const contentTemplate = '<div>[BID_RESULT]</div>';
const nRequests = 10;
var requestCnt = 0;
var content = '';
function checkBidStatus(index) {
return new Promise((resolve, reject) => {
$.ajax({
type:'get',
url:'/orderbook/checkorderdetails',
success: function(data){
$parsedData = JSON.parse(data);
resolve({ index: index + 1, msg: $parsedData ? "Hello" : "Bye" });
},
error: reject
});
// <-- NOTE: there is no return statement at this line. Hence if
// you were to call this function from your for loop, you'd see
// undefined
}
}
Then, you can keep calling that function recursively:
function multipleRequests(){
checkBidStatus(requestCnt)
.then((resp) => { // This runs if the promise RESOLVES (i.e., success)
content = contentTemplate.replace('[BID_RESULT]', resp.msg);
// --- [then do what you want with content] --- //
// Request again
requestCnt++;
if( requestCnt < nRequests ){
multipleRequests();
}
})
.catch(console.error) // This runs if promise rejects (i.e., has an error)
}
// Run:
multipleRequests();
Why not use a Promise inside and wait on that promise to get the required result, after which you update the DOM? In your checkBidStatus method, you can create a promise and return that. When the ajax result is available, resolve the promise with ajax result.
The caller of the method would need to wait for the promise to resolve and then update the DOM.
let content = '';
for(let i = 1; i <= 10; i++ ) {
checkBidStatus(i).then(result => {
content += '<div>' +result + '</div>';
})
}
Note: I have not tested the code, but something similar should work. Also the order of divs created base on the index is not guaranteed, so you may need to take care of that, if order is important.
You should wait till response from server for every iteration in loop.
function checkBidStatus(index) {
$.ajax({
type: 'get',
url: '/orderbook/checkorderdetails',
success: function(data) {
$parsedData = JSON.parse(data);
if ($parsedData[index]) {
return {
msg: 'Hello',
index: index + 1,
};
} else {
return {
msg: 'Bye',
index: index + 1,
};
}
//$("#bid-details .modal-dialog").html(data);
},
error: function(response) {
console.log(response);
},
});
}
let content = '';
let i = 1;
while (i <= 10) {
let { msg, index } = checkBidStatus(i);
content += '<div>' + msg + '</div>';
i = index;
}

Function in while loop executes only once

I am a beginner in javascript, and I'm trying to figure out why my while loop won't actually loop more than once, even though the condition is always met.
I have a function sending an API request:
var get_status = function(trid, count) {
console.log(count);
var req = {
method: 'GET',
url: 'theUrlHere',
headers: {'headers'}
}
$http(req).success(function(data) {
if (data.transaction_status != 'Pending') {
// do something with the data
console.log('true');
return true;
}
else {
console.log('False');
return false;
}
}).error(function(data) {
// show an error popup
console.log('true');
return true;
})
}
};
I want to call this function until it returns true, so I call it this way:
var count = 0;
while (get_status(id, count) === false) {
count += 1;
}
The count variable is just added to see how many times it loops, it stays at 0 even though 'False' is displayed in the console.
Is there some behaviour I am misunderstanding here?
EDIT I understand why this won't work. My intention here is to display an iframe as long as the transaction status is pending. I thought of continually sending a request until the transaction status is something other then 'Pending', but I am aware there are more optimal ways.
Your get_status() function does not return a value. Thus, it's return value is undefined which is falsey so your while() loop stops after the very first iteration.
The return statements you do have in your code are inside of callbacks and have nothing to do with the return value of get_status().
What you are attempting to do is generally not a good design. It appears that you want to run a given Ajax call over and over with no delay until you get the answer you want. This will potentially hammer the destination server.
If you describe the problem you're really trying to solve, we could help come up with a better way to do this. Worst case, you could poll the server with a time delay between requests.
If you wanted to poll every so often, you could do something like this:
function get_status(trid, count) {
var req = {
method: 'GET',
url: 'theUrlHere',
headers: {'headers'}
}
return $http(req).then(function(data) {
return data.transaction_status;
});
}
function poll_status(callback) {
function next() {
get_status(...).then(function(status) {
if (status === "Pending") {
// poll once every two seconds
setTimeout(next, 2000);
} else {
// status is no longer pending, so call the callback and pass it the status
callback(status);
}
}, function(err) {
callback(err);
});
}
next();
}
poll_status(function(result) {
// done polling here, status no longer Pending
});
This is not the correct way to deals with async calls, I'd create a recursive function which will call itself. (in this case get_status should return a promise)
Code
var count = 0, id = 1;//id should be some value
(function myCall(promise){}
promise.then(function(data){
count += 1;
if(data)
myCall(get_status(id, count)); //call function on conditon
});
}(get_status(id, count))
Method(Returning Promise)
var get_status = function(trid, count) {
console.log(count);
var req = {
method: 'GET',
url: 'theUrlHere',
headers: {'headers'}
}
//returning promise here
return $http(req).then(function(response) {
var data = response.data;
if (data.transaction_status != 'Pending') {
// do something with the data
console.log('true');
return true; //resolves the promise
}
else {
console.log('False');
return false; //resolves the promise
}
}, function(data) {
// show an error popup
console.log('true');
return true;
})
}
};
You're trying to return from within an asynchronous callback, which won't work, unfortunately. Instead you'll want a module like async, specifically whilst.
var count = 0;
var outcome = false;
async.whilst(
function () { outcome = false; },
function (callback) {
count++;
// Your code here, setting outcome instead of returning
var req = {
method: 'GET',
url: 'theUrlHere',
headers: {'headers'}
}
$http(req).success(function(data) {
if (data.transaction_status != 'Pending') {
outcome = true;
callback();
}
else {
outcome = false
callback();
}
}).error(function(data) {
outcome = true;
callback();
})
},
function (err) {
// All done!
}
);
But really the behavior you're looking for is probably checking on a status at pre-defined intervals. In this case, adapting the code
var count = 0;
var outcome = false;
async.whilst(
function () { outcome = false; },
function (callback) {
count++;
// Your request stuff.
setTimeout(function () {
callback();
}, 1000); // Waits one second to begin next request
},
function (err) {
// All done!
}
);

Categories

Resources