get a return value from setInterval function - javascript

i have an ajax function which gets called and returns json data. this function is called via a setInterval loop:
function SetMaxBidReload() {
var reloadInt = 6000;
var doReload = true;
//set some logic here
if (doReload) {
setInterval(function () { ReloadMaxBid(); }, reloadInt);
}
}
function ReloadMaxBid() {
var nextReload;
$.ajax({
url: 'ajaxcall.aspx',
cache: false,
success: function (data) {
//change to dom elemants here based on return data
nextReload = data[0].NextReload;
}
});
return nextReload;
}
what im trying to do is change the reloadInt of setInterval based on what comes back from the ajax call.
a: is that possible and b: how do i do it?
im open to suggestions on how to accomplish this by coding it differently

setInterval takes the timeout param once and then executes the function based on that interval, changing the reloadInt wouldn't have any effect whatsoever.
You need to use setTimeout in the success callback:
function ReloadMaxBid() {
$.ajax({
url: 'ajaxcall.aspx',
cache: false,
success: function (data) {
//change to dom elemants here based on return data
var next = data[0].NextReload;
if (next !== -1) {
setTimeout(function(){ReloadMaxBid();}, next);
}
}
});
}
You'd also need to use setTimeout with the value of 6000 somewhere to get things started.

Related

JQuery Ajax Anonymus Callback Function gets overwritten when called while ajax in progress

I have a timing or scope problem on this function call.. or no idea what.
AjaxHandlerByClass('url', {clientName: this.clientName}, function (response) { this code gets never called})
AjaxHandlerByClass('url', {clientName: this.clientName}, function (response) { This code gets called 2 times})
From this Function
function AjaxHandlerByClass(className, postData, callback, callbackFail) {
var timestamp = new Date().getTime();
var me = this;
me.backHandler = function (data) {
if (data)
if (data.responseJSON || data.debug) {
if (data.debug)
var debug = data.debug;
else if (data.responseJSON && data.responseJSON.debug)
var debug = data.responseJSON.debug;
if (window.console) {
for (var key in debug) {
if (debug.hasOwnProperty(key)) {
// console.log(debug[key]);
}
}
}
}
if (me.mode = 'callback') {
callback(data); //<--- this is the bug location
} else {
callbackFail(data);
}
};
this.ok = function (data) {
me.mode = 'callback';
me.backHandler(data)
}
this.notOk = function (data) {
me.mode = 'callbackFail';
me.backHandler(data)
}
$.ajax(
{
contentType: "application/json; charset=utf-8",
url: className + '?ts=' + timestamp + '&sid=' + sid,
type: 'post',
data: JSON.stringify(postData),
dataType: 'json',
cache: false,
success: me.ok,
error: me.notOk
}
);
}
The first callback Function never gets executed, while the second one does get executed but 2 times.
The bug happens on the if (me.mode = 'callback') part of the code.
I already tried other options to make the callback function stuck right.
The first attempt was to store the callback function in the Function scope itself.
with assigning it to this.callback and then trying to access it via me.scope
which did not work. then I tried to access the variables directly.. and it is not helping either...
this.callback = callback;
this.callbackFail = callbackFail;
var me = this;
me.backHandler = function (data) {
if (data)
if (data.responseJSON || data.debug) {
if (data.debug)
var debug = data.debug;
else if (data.responseJSON && data.responseJSON.debug)
var debug = data.responseJSON.debug;
if (window.console) {
for (var key in debug) {
if (debug.hasOwnProperty(key)) {
// console.log(debug[key]);
}
}
}
}
me[me.mode](data);
};
I'm on my wit's end.
First of, callBackFail is never defined in your code.
function AjaxHandlerByClass(className, postData, callback, callbackFail)
You pass in three parameters: className, postData and callback.
AjaxHandlerByClass('url', {clientName: this.clientName}, function (response) { this code gets never called})
Second, this line should be me.mode === 'callback', not me.mode = 'callback'
if (me.mode === 'callback') {
callback(data);
} else {
callbackFail(data);
}
You've named your function AjaxHandlerByClass, I assume you want to use it as a class. You've declared it as function. Regular functions are executed when they are invoked(called) causing the second call to AjaxHandlerByClass() to render twice. To solve your problem you could either create a new instance of your AjaxHandlerByClass using the new keyword.
const firstRequst = new AjaxHandlerByClass('url', {clientName: "https://swapi.co/api/people/"}, successCallback, failCallback);
const secondRequst = new AjaxHandlerByClass('url', {clientName: "https://swapi.co/api/planets/"}, successCallback, failCallback);
Or if you want to wait for the first request to finish before calling the second request you could implement Promise. More on Promise here.
I created a js-fiddle here with some modifications(swapped api and renamed some varaibles just for testing purpose.). One of the ajax-request is successful and the other fails. The result is visible in the developer-console. Note this fiddle is not perfectly written, its just some dummy code for demo purpose.

Wait for $.ajax result inside .each function

I have function that search for every element with a specific class:
$("#stepSurveyCtnId .questionCtnClass").each(function () {}
Inside each step, I check if a question is of type customer:
var type = $(this).children().data("question-type");
var isCustomerQuestion = false;
switch (type) {
case "Name":
case "Email":
isCustomerQuestion = true;
break;
}
If it's customer type, I get the next id of the customer's table from the database:
if(isCustomerQuestion) {
if (customerId == -1) {
$.ajax({
method: "POST",
url: urlCustomerCreate,
success: function (ajaxData) {
customerId = ajaxData.NumericValue;
}
});
}
}
The issue is that in the second iteration of the .each() function, customerId is still = -1, when it should be 1305 for example.
It seems that the execution don't stop in the $.ajax call, or the iterations are executed at the same time and the second iteration don't receive the customerId from the first iteration.
I'm still not 100% clear on sure on how everything is structured for you, but here is one way of handling asynchronicity in JavaScript (adapted from #ShubHam's answer)
function handleQuestion(questionElements, index, customerId) {
if (questionIndex >= questionElements.length) return;
var type = $(this).children().data("question-type");
var isCustomerQuestion = false;
switch (type) {
case "Name":
case "Email":
isCustomerQuestion = true;
break;
}
if(isCustomerQuestion) {
if (customerId == -1) {
$.ajax({
method: "POST",
url: urlCustomerCreate,
success: function (ajaxData) {
handleQuestion(questionElements, questionIndex + 1, ajaxData.NumericValue);
}
});
} else {
// Have ID now
handleQuestion(questionElements, questionIndex + 1, customerId);
}
}
}
// Go
handleQuestion($("#stepSurveyCtnId .questionCtnClass"), 0, -1);
This will only continue to the next iteration after the success callback has been triggered.
Put logic inside one function (say function 1) and ajax call inside other function.
Call ajax function from function 1. Inside success call function 1 with required params
Update (example added):
var x=['a','b','c']
var elm=document.getElementById('demo')
x.forEach(function(temp){
elm.innerHTML=elm.innerHTML+temp
})
<div id='demo'></div>
This can be converted to new logic as
var x=['a','b','c']
function sethtml(temp,length,maxlength){
//here ajax call can be placed
var elm=document.getElementById('demo')
elm.innerHTML=elm.innerHTML+temp
//inside success function of ajax
traverse(length+1,maxlength)
}
function traverse(length,maxlength){
if(length>=maxlength)
{
//all calls done next steps to perform
}else{
sethtml(x[length],length,maxlength)
}
}
traverse(0,x.length)
<div id='demo'></div>
Advice to be considered from Jamie-Day in comments: Check your logic for scope of improvement. Accessing db results in for each kind of scenario generally can be avoided(ideally it should be avoided for better user experience)
Change your ajax code. add "async: false" so that each code next to ajax will wait for ajax result
if(isCustomerQuestion) {
if (customerId == -1) {
$.ajax({
method: "POST",
async: false,
url: urlCustomerCreate,
success: function (ajaxData) {
customerId = ajaxData.NumericValue;
}
});
}
}
First, you need to think asynchronously.
Code that need to run after the ajax should be called from the success function. You also want to add error function to handle server errors.
Second, to improve speed and bandwidth I'd reduce number of AJAX calls to a single one, by joining all IDs together in a single AJAX request.
It require server-side changes and you did not provide the server-side, so I'll leave server side to you.
// Prepare ajax call
var customerData = [];
var customerCreateData = [];
$("#stepSurveyCtnId .questionCtnClass").each(function () {
var type = $(this).children().data("question-type");
var isCustomerQuestion = false;
switch (type) {
case "Name":
case "Email":
isCustomerQuestion = true;
break;
}
// Set customerId and customerCreateData
if(isCustomerQuestion) {
if (customerId == -1) {
customerCreateData.push(customerCreateData);
}
}
}); // end each
if (customerCreateData.length) {
$.ajax({
method: "POST",
url: urlCustomerCreate,
data: customerCreateData,
success: function (ajaxData) {
customerData = ajaxData.customerData;
doAmazingThingsWithCustomers(customerData);
},
error: function(jqXHR, textStatus, errorThrown) {
alert('Server error: ' + errorThrown);
}
});
}
The first A in AJAX stands for Asynchronous which means that the ajax calls would get executed and would not wait for the call to finish. This way we can let users interact with other elements on the page and provide a good user experience.
If we make the AJAX calls asynchronous by setting the async option to false, then the browser would wait for the ajax call to complete and users would not be able to interact with the elements until the call has completed. With the increase in number of calls, this blockage time would increase.
I would suggest you find a better alternative than this.

Return values from an Ajax function

I am trying to create a block of text that will update itself when the text changes from a Json string.
Basically I started with:
function streamSong(index) {
if (!isUndefined(myPlaylist[index].title))
return myPlaylist[index].title;
else return '';
}
then modified it to look like this:
function streamSong(index) {
var currentSongName = 'here';
if (!isUndefined(myPlaylist[index].title)) {
var intervalFunc = function(){
var jsonData = null;
$.ajax({
url: 'http://www.thesite.com/pullJson.php?stream=rapstation',
dataType: "json",
data: { get_param: 'employees' },
success: function (data) {
currentSongName = 'now here';
},
error: function (data) {
currentSongName = 'not working';
}
});
};
setInterval (intervalFunc, 60000);
setTimeout (intervalFunc, 1);
return currentSongName;
}
else return 'no title';
}
The first function fired off fine and returned my Stream Title.
The second function fires off, but I never am able to modify the value of currentSongName.
I am still a bit new to Javascript and ajax so excuse my ignorance, but I obviously want to ultimately set the value of currentSongName to the Json value I retrieve, but for now I would just like it to be able to change values on a timer.
Am I going about this all wrong?
The variable is modified just fine, but too late. The AJAX call is asynchronous, so the variable is used to return the value before the value is assigned to it.
You would use a callback to handle the result. With the original code it would look like this:
function streamSong(index, callback) {
if (!isUndefined(myPlaylist[index].title)) {
callback(myPlaylist[index].title);
} else {
callback('');
}
}
Usage:
streamSong(42, function(title) {
// do what you want with the title
});
For the AJAX call the callback would be used like this:
function streamSong(index, callback) {
var currentSongName = 'here';
if (!isUndefined(myPlaylist[index].title)) {
var intervalFunc = function(){
var jsonData = null;
$.ajax({
url: 'http://www.thesite.com/pullJson.php?stream=rapstation',
dataType: "json",
data: { get_param: 'employees' },
success: function (data) {
callback('now here');
},
error: function (data) {
callback('not working');
}
});
};
setInterval (intervalFunc, 60000);
setTimeout (intervalFunc, 1);
} else {
callback('no title');
}
}

Accessing outer scope

I'm working on creating a Users collection with the ability to then grab single users inside. This will be used to match from another system, so my desire is to load the users once, and then be able to fine/match later. However, I'm having a problem accessing the outer users collection from an inner method.
function Users(){
var allUsers;
this.getUsers = function () {
// ajax to that Jasmine behaves
$.ajax({
url: '../app/data/jira_users.json',
async: false,
dataType: 'json',
success: function(data) {
allUsers = data;
}
});
return allUsers;
};
this.SingleUser = function (name) {
var rate = 0.0;
var position;
this.getRate = function () {
if(position === undefined){
console.log('>>info: getting user position to then find rate');
this.getPosition();
}
$.ajax({
url: '../app/data/rates.json',
async: false,
dataType: 'json',
success: function(data) {
rate = data[position];
}
});
return rate;
};
this.getPosition = function () {
console.log(allUsers);
//position = allUsers[name];
return position;
};
//set name prop for use later I guess.
this.name = name;
};
}
and the test that's starting all of this:
it("get single user's position", function(){
var users = new Users();
var someone = new users.SingleUser('bgrimes');
var position = someone.getPosition();
expect(position).not.toBeUndefined();
expect(position).toEqual('mgr');
});
The getPosition method is the issue (which might be obvious) as allUsers is always undefined. What I have here is yet another attempt, I've tried a few ways. I think the problem is how the Users.getUsers is being called to start with, but I'm also unsure if I'm using the outer and inner vars is correct.
Though the others are correct in that this won't work as you have it typed out, I see the use case is a jasmine test case. So, there is a way to make your test succeed. And by doing something like the following you remove the need to actually be running any kind of server to do your test.
var dataThatYouWouldExpectFromServer = {
bgrimes: {
username: 'bgrimes',
show: 'chuck',
position: 'mgr'
}
};
it("get single user's position", function(){
var users = new Users();
spyOn($, 'ajax').andCallFake(function (ajaxOptions) {
ajaxOptions.success(dataThatYouWouldExpectFromServer);
});
users.getUsers();
var someone = new users.SingleUser('bgrimes');
var position = someone.getPosition();
expect(position).not.toBeUndefined();
expect(position).toEqual('mgr');
});
This will make the ajax call return whatever it is that you want it to return, which also allows you to mock out tests for failures, unexpected data, etc. You can set 'dataThatYouWouldExpectFromServer' to anything you want at any time.. which can help with cases where you want to test out a few different results but don't want a JSON file for each result.
Sorta-edit - this would fix the test case, but probably not the code. My recommendation is that any time you rely on an ajax call return, make sure the method you are calling has a 'callback' argument. For example:
var users = new Users();
users.getUsers(function () {
//continue doing stuff
});
You can nest them, or you can (preferably) create the callbacks and then use them as arguments for eachother.
var users = new Users(), currentUser;
var showUserRate = function () {
//show his rate
//this won't require a callback because we know it's loaded.
var rate = currentUser.getRate();
}
var usersLoaded = function () {
//going to load up the user 'bgrimes'
currentUser = new users.SingleUser('bgrimes');
currentUser.getRate(showUserRate);
}
users.getUsers(usersLoaded);
your approach to fill the data in allUsers is flawed
the ajax call in jquery is async so every call to users.getAllUsers would be returned with nothing and when later the success function of the jquery ajax is called then allUsers would get filled
this.getUsers() won't work. Its returning of allUsers is independent from the ajax request that fetches the data, because, well, the ajax is asynchronous. Same with getRate().
You'll have to use a callback approach, where you call getUsers() with a callback reference, and when the ajax request completes, it passes the data to the callback function.
Something like:
this.getUsers = function (callback) {
// ajax to that Jasmine behaves
$.ajax({
url: '../app/data/jira_users.json',
async: false,
dataType: 'json',
success: function(data) {
callback(data);
}
});
};
And the call would be along the lines of:
var user_data = null;
Users.getUsers(function(data) {
user_data = data;
});

get a callback function to add to object javascript

I have an issue with a method ive created for an object ive created. one of the methods requires a callback to another method. the problem is i cant add the data to the object that called the method. it keeps coming back as undefined. otherwise when i send the data to the console it is correct. how can i get the data back to the method?
var blogObject = new Object();
var following = [...];
//get posts from those blogs
blogObject.getPosts = function () {
var followersBlogArray = new Array();
for (var i = 0; i < this.following.length;i++){
var followersBlog = new Object();
// get construct blog url
var complete_blog_url = ...;
i call the getAvatar function here sending the current user on the following array with it.
followersBlog.avatar = blogObject.getAvatar(this.following[i]);
that part goes smoothly
followersBlogArray.push(followersBlog);
}
this.followersBlogArray = followersBlogArray;
}
here is the function that gets called with the current user in following array
this function calls an ajax function
blogObject.getAvatar = function (data) {
console.log("get avatar");
var url = "..."
this ajax function does its work and has a callback function of showAvatar
$(function() {
$.ajax({
type: "GET",
dataType: "jsonp",
cache: false,
url: url,
data: {
jsonp:"blogObject.showAvatar"
}
});
});
}
this function gets called no problem when getAvatar is called. i cant however get it to add the data to the followersBlog object.
blogObject.showAvatar = function (avatar) {
return avatar
}
everything in here works fine but i cant get the showAvatar function to add to my followersBlog object. ive tried
blogObject.showAvatar = function (avatar) {
this.followersBlog.avatar = avatar;
return avatar
}
that didnt work of course. it shows up as undefined. can anyone help?
so somethings like...
$(function() {
$.ajax({
type: "GET",
dataType: "jsonp",
cache: false,
url: url,
complete: function () {
this.avatar = data;
}
data: {
jsonp:"blogObject.showAvatar"
}
});
});
}
Welcome to the world of asynchronous programming.
You need to account for the fact that $.ajax() will not return a value immediately, and Javascript engines will not wait for it to complete before moving on to the next line of code.
To fix this, you'll need to refactor your code and provide a callback for your AJAX call, which will call the code that you want to execute upon receiving a response from $.ajax(). This callback should be passed in as the complete argument for $.ajax().
The correct option for setting the JSONP callback is jsonpCallback. The recommendation from the API for .ajax(...) is to set it as a function.
{
// ...
jsonpCallback: function (returnedData) {
blogObject.showAvatar(returnedData);
},
// ...
}

Categories