Handling multiple setinterval javascript [duplicate] - javascript

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 7 years ago.
So I'm trying to build a web application that will ping a device's registers for stored data every 30 seconds.
as you can see each device can have multiple registers. When a user creates a new device I iterate all the newly created register id's that I return from my ajax post(generated DB id's) and then start an interval timer making an ajax call every 30 seconds to a method that will ping my device and get the data for the specific register.
The problem I'm running into is that every time the interval runs my ajax call, it's reusing the last register id to fetch data instead of running a fetch on each individual register. Ex. my 2 rows have an id of 22 and 23. Everytime my interval function is called it will use id 23 and make the ajax call instead of calling 22 then 23. Is this because I'm using a for loop when instantiating a new interval?
Here is how I try to handle my setInterval:
var registers = result.register_ids;
for (var i = 0; i < registers.length; ++i) {
debugger;
var interval = setInterval(function () { fetchRegisterValues(registers[i], result.modbus_id) }, 30000);
register_ping_threads[registers[i]] = interval;
}
Here is my ajax call to read the specified register:
function fetchRegisterValues(register_id, modbus_id) {//id always ends up being 23
debugger;
$.ajax({
type: "POST",
url: "/fetch_mdata",
data: {
'csrfmiddlewaretoken': token,
'register_id': register_id, //use to read the register range
'modbus_id': modbus_id //used to get device connectiong settings
},
contentType: "application/x-www-form-urlencoded",
dataType: 'json',
success: function (result) {
debugger;
$('[data-register="' + register_id + '"]').find('[data-value=""]').text(result.value);
},
error: function (data) {
debugger;
$('#loading-icon').hide()
$('#ping_error').addClass('alert alert-danger');
$('#ping_error strong').append('Problem contacting server..');
}
});
}

This is a very common mistake in JavaScript. Remember that i is in a scope of a function, not the scope of the loop, so all your fetchRegisterValues will reuse the same value of i. To fix it, create a new scope:
for (var i = 0; i < registers.length; ++i) {
debugger;
var interval = setInterval((function (i) { return function() { fetchRegisterValues(registers[i], result.modbus_id) } })(i), 30000);
register_ping_threads[registers[i]] = interval;
}

Related

How to loop through GET/POST calls sequentially (waiting for previous) return?

I'm writing a Tampermonkey script for a web page and trying to extract data from other pages.
I'm trying to make a function that has a loop inside that goes thru a list, llcList, and retrieves data from ajax method GET, but would like to wait for to finish one request before going to second one.
Bonus would be if I could make it wait some extra time.
What should happen:
send request for a llcList[0]
get return data, process it
wait some time
send new request for a llcList[1]
Is this possible? I tried few methods, every time loop send all requests not a second apart. :
function F_Company_LLC(){
for (i = 0; i < llcList.length;i++) {
if(llcList[i][2]=="lab"){
//run function 0
//break;
}
else if(llcList[i][2]=="shop"){
//run function 1
//break;
}
else{
F_GET_CompData(llcList, llcList[i][1],i,function(result){
console.log(result);
});
}
}}
function F_GET_CompData(F_GET_CompData_list, CompID, F_GET_CompData_row, callback){
$.ajax({
method : "GET",
url: base_link+"/company/edit_company/"+CompID,
beforeSend: function(){runningRequest++;},
success: function(data){
//data processing
runningRequest--;
},
error: function() {console.log("Get_ComData");}
});
callback(runningRequest);}
This is a common scenario. Note that it's often unnecessary to process the calls sequentially though. It's usually adequate to just send context with the ajax calls and piece everything together as it comes in semi randomly, as shown in this answer.
One way to force sequential behavior is to chain calls via the complete function. Here is fully functional code that demonstrates the process. To use, paste it into your browser console while on a Stack Overflow page. :
var listO_pages = ["q/48/", "q/27/", "q/34/", "q/69/", "badpage"];
var numPages = listO_pages.length;
getPageN (0); //-- Kick off chained fetches
function getPageN (K) {
if (K >= 0 && K < numPages) {
let targPage = listO_pages[K];
$.ajax ( {
url: "https://stackoverflow.com/" + targPage,
context: {arryIdx: K}, // Object Helps handle K==0, and other things
success: processPage,
complete: finishUpRequest,
error: logError
} );
}
}
function processPage (sData, sStatus, jqXHR) {
//-- Use DOMParser so that images and scripts don't get loaded (like jQuery methods would).
var parser = new DOMParser ();
var doc = parser.parseFromString (sData, "text/html");
var payloadTable = doc.querySelector ("title");
var pageTitle = "Not found!";
if (payloadTable) {
pageTitle = payloadTable.textContent.trim ();
}
var [tIdx, tPage] = getIdxAndPage (this); // Set by `context` property
console.log (`Processed index ${tIdx} (${tPage}). Its title was: "${pageTitle}"`);
}
function finishUpRequest (jqXHR, txtStatus) {
var nextIdx = this.arryIdx + 1;
if (nextIdx < numPages) {
var tPage = listO_pages[nextIdx];
//-- The setTimeout is seldom needed, but added here per OP's request.
setTimeout ( function () {
console.log (`Fetching index ${nextIdx} (${tPage})...`);
getPageN (nextIdx);
}, 222);
}
}
function logError (jqXHR, txtStatus, txtError) {
var [tIdx, tPage] = getIdxAndPage (this); // Set by `context` property
console.error (`Oopsie at index ${tIdx} (${tPage})!`, txtStatus, txtError, jqXHR);
}
function getIdxAndPage (contextThis) {
return [contextThis.arryIdx, listO_pages[contextThis.arryIdx] ];
}
This typically outputs:
Processed index 0 (q/48/). Its title was: "Multiple submit buttons in an HTML form - Stack Overflow"
Fetching index 1 (q/27/)...
Processed index 1 (q/27/). Its title was: "datetime - Calculate relative time in C# - Stack Overflow"
Fetching index 2 (q/34/)...
Processed index 2 (q/34/). Its title was: "flex - Unloading a ByteArray in Actionscript 3 - Stack Overflow"
Fetching index 3 (q/69/)...
Processed index 3 (q/69/). Its title was: ".net - How do I calculate someone's age in C#? - Stack Overflow"
Fetching index 4 (badpage)...
GET https://stackoverflow.com/badpage?_=1512087299126 404 ()
Oopsie at index 4 (badpage)! error Object {...
-- depending on your Stack Overflow reputation.
Important: Do not attempt to use async: false techniques. These will just: lock up your browser, occasionally crash your computer, and make debug and partial results much harder.

How to call a JS function every 10 sec which then activates a function in angular

I am trying to use a highcharts chart and I want to "simulate" live data coming in, so, when a user presses the "start live stream" button it activates a function from I suppose JavaScript on the web page and then calls the angular controller function that has around 10 second delay.
The way I can query the json data from the controller is from an http request and I use how far back in weeks I want to query the data (I have as far back as 100 weeks). So I want to have a function on the web page the starts at 99 and 100 and pass in the variable to the angular function to query from 100-99 weeks ago and add the data to the chart. Wait 10 seconds and query now instead 99-98 until it gets to zero.
I am pretty new to JS in general so I'm not too sure how to start but I have read about the setTimeout function. Any suggestions or better way to go about this would be much appreciated.
My current http request looks like this and is static:
$http({
url: '/api/v1/datapoints',
method: 'POST',
data: '{"start":"99w-ago","end":"98w-ago","tags":[{"name":"SolarData"}]}'
}).then(function(predixTimeSeriesData){
$scope.solarData = predixTimeSeriesData.data.tags[0].results[0].values.map(
function(curVal, index, arr) {
return [curVal[0], curVal[1]];
}
);
console.log($scope.solarData);
/*
I use $scope.solatData in my chart on the html page like
<line-series-chart data={{solarData}}></line-series-chart>
so this is why I am thinking I need to have the time interval on the view page
instead of the controller because i cannot control my chart from there
*/
});
You can use the $interval service of angular, something like this:
function myController($scope, $http, $interval) {
var currentWeek = 99;
var fetchInterval;
$scope.solatData = [];
$scope.fetch = function() {
$http.get("someUrl", {
params: {
week: currentWeek
}
}).then(function(data){
// This will also update your graph, assuming it is implemented
// to watch changes on the data
$scope.solatData = $scope.solatData.concat(data);
currentWeek++;
});
}
$scope.start = function() {
fetchInterval = $interval($scope.fetch, 10000);
}
// Clear the interval when the scope/controller is 'destroyed'
$scope.$on('$destroy', function() {
$interval.cancel(fetchInterval);
});
// kick off initial start
$scope.start();
}

How to get and append most recent messages from server using jQuery and AJAX?

I'm working on my first simple chat application and this issue has me stuck. I know what I'm trying to do, but I end up overthinking it.
Basically, I have this heroku server going:
http://tiy-fee-rest.herokuapp.com/collections/blabberTalk
Whenever someone sends a message, it is added to this array.
My Issue:
I have it on a set interval so that every 2 seconds, it runs the getNewestMessages function. When this setInterval is working and someone sends a message, it will keep appending the last message they sent every 2 seconds. If I disable the setInterval and simply call the getNewestMessages function myself in a separate browser tab, this doesn't seem to happen. I want to make it so that the most recently sent message isn't constantly re-appended to the DOM when the setInterval is active.
This is the function I'm using to check for recent messages. It's pretty bloated, sorry about that:
getNewestMessages: function() {
$.ajax({
url: http://tiy-fee-rest.herokuapp.com/collections/blabberTalk,
method: 'GET',
success: function (data) {
// Finds Id of most recent message displayed in the DOM
var recentId = $('.message').last().data('id');
var prevMostRecent = 0;
var newMostRecent = [];
jQuery.each(data, function(idx,el){
if (el._id === recentId) {
// if one of the messages on the server has an Id equal to
// one of the messages in the DOM, it saves its index in a var
prevMostRecent = idx;
}
});
jQuery.each(data, function(idx,el){
if (idx < prevMostRecent) {
// if there are messages on the server with a lower index than
// the most recent message in the DOM, it pushes them to a new
// array. Basically, creates a new array of any messages newer
// than the last one displayed in the DOM.
newMostRecent.push(el);
}
});
for (var i = 0; i < newMostRecent.length; i++) {
console.log(newMostRecent[i]);
if (newMostRecent[i]._id === $('.message').last().data('id')) {
// My attempt at trying to remove the last DOM message from
// the array of newer messages. My main issue was that this
// whole function would keep appending the most recent message
// over and over again.
var result = _.without(newMostRecent, newMostRecent[i]);
console.log('MESSAGE TO BE EXCLUDED: ', newMostRecent[i]);
// If the array of newer messages contained the most recent
// DOM message, it removes it and sends it to be appended.
page.appendNewestMessages(result);
}
}
// If the array of newer messages DOESN'T contain the most recent
// DOM message, it just sends the whole array normally.
page.appendNewestMessages(newMostRecent);
},
error: function (err) {
}
});
}
Here is the append function:
appendNewestMessages: function(messagesToAppend) {
console.log(messagesToAppend.reverse());
_.each(messagesToAppend.reverse(), function(el, idx, arr) {
var newMessage = {
content: el.content,
timestamp: el.timestamp,
author: el.author,
userIcon: el.userIcon
}
$.ajax({
url: page.url,
method: 'POST',
data: newMessage,
success: function (data) {
page.addOneMessageToDOM(data);
},
error: function (err) {
console.log("error ", err);
}
});
})
}
Can anyone help me understand how to get the most recent messages from a server and append them to the DOM without any repeats? This has been driving me nuts.
Thanks for any and all help.

Iterate array in Javascript with Sleep

Hi I am making one site in RubyOnRails. I am having problem in showing some content at client side. What I want to do is like news where after every 10 sec., news would change. What I have done is I have make an ajax which fetch the news from my server, server returns array in json response. Now I have all the news array at client side I want to show one by one in 10 sec interval.
I have tried with this code but its not showing anything except last news.
function get_news(){
$.ajax({
url : "my.json",
success:function(data){
// data is array of json like [{"html" : "dfsf"},{"html":"ddd"}]
news_change(data);
}
});
}
get_news();
function news_change(feed){
$.each(feed,function(index,f){
var feed_html = f.html;
$('#news_div').fadeOut('slow', function(){
$('#news_div').html(feed_html);
$('#news_div').fadeIn('slow');
});
sleep(10000);
});
}
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
when I execute this code it only shows news which is last. And also it hang my browser. Please suggest me why is this because ?
Use setTimeout or setInterval, which execute code asynchronously after a certain number of milliseconds. Looping is synchronous and locks the browser while it executes.
// this will execute get_news every 10,000 ms
var newsInterval = setInterval(get_news, 10000);
// if you ever want to stop the interval, use clearInterval
clearInterval(newsInterval);
Note that get_news performs an ajax call, which could take some time, meaning that your news will not update exactly every 10 seconds.
EDIT: to iterate through the news array every 10 seconds, you'd pass the news_change function to setInterval:
var newsInterval;
function get_news(){
$.ajax({
url : "my.json",
success:function(data) {
newsInterval = setInterval(function () {
news_change(data);
}, 10000);
}
});
}
get_news();
// if you ever want to stop the interval, use clearInterval
clearInterval(newsInterval);

Trying to increment a javascript counter when using polling ajax http requests

I have a script that is running every 5 seconds, as a polling ajax http request.
I want to send an incrementing amount through it, on each request (1,2,3,4,5,6 etc...)
I have this so far, but the code just sends '1' through all the time.
// set the value to 1
var state = {
recordId: 1
};
$.ajaxPoll({
url: "map-service.cfc?method=getpoints",
type: "POST",
data: state,
dataType: "json",
successCondition: function(location) {
// return result != null; // custom condition goes here.
// increment it by 1
state.recordId = state.recordId +1
alert(state.recordId)
}
Does anyone know how to submit an increasing value through the 'data' param in the POST?
You have to make sure you are not setting the state.recordId over and over again each time you execute the function that calls the .ajaxPoll() method. state = ... should be in a parent scope of the function that runs .ajaxPoll() maybe something like this:
(function() { // <== Our scope
// set the value to 1 only once!
var state = {
recordId: 1
};
// The function that runs the poll. Do not set recordId to 1 inside here!
var callPoll = function() {
$.ajaxPoll({
url: "map-service.cfc?method=getpoints",
type: "POST",
data: state, // state.recordId will be 1, 2, 3, ...
dataType: "json",
successCondition: function(location) {
// return result != null; // custom condition goes here.
// increment it by 1
state.recordId = state.recordId +1
alert(state.recordId)
}
});
};
$(function() { // <== on doc ready
// Your functionality etc...... for example:
setInterval(callPoll, 1000);
});
}()); // <== Execute the anonymous function we're using as our scope
Probably a copy of state object is closed inside the anonymous function which will always start with it's initial value unless the changes are made outside of closure before it's closed/created. For verification just replace that incremental line with following
window.recordId = window.recordId + 1 // Making it global variable
You can find a very basic introduction to closures here http://msdn.microsoft.com/en-us/magazine/cc163419.aspx
Can you pass state object as a variable to successCondition anonymous function? That way you will always get the actual copy
You could also set a data variable to the document or body and increment that.
$('body').data('recordId', 1);
$.ajaxPoll({
url: "map-service.cfc?method=getpoints",
type: "POST",
data: {recordId: $('body').data('recordId')},
dataType: "json",
successCondition: function(location) {
// return result != null; // custom condition goes here.
// increment it by 1
newRecord = $('body').data('recordId');
$('body').data('recordId', newRecord+1);
alert($('body').data('recordId'));
}
});

Categories

Resources