Why is setInterval calling a function with random arguments? - javascript

So, I am seeing a curious problem. If I have a function
// counter wraps around to beginning eventually, omitted for clarity.
var counter;
cycleCharts(chartId) {
// chartId should be undefined when called from setInterval
console.log('chartId: ' + chartId);
if(typeof chartId == 'undefined' || chartId < 0) {
next = counter++;
}
else {
next = chartId;
}
// ... do stuff to display the next chart
}
This function can be called explicitly by user action, in which case chartId is passed in as an argument, and the selected chart is shown; or it can be in autoplay mode, in which case it's called by a setInterval which is initialized by the following:
var cycleId = setInterval(cycleCharts, 10000);
The odd thing is, I'm actually seeing the cycleCharts() get a chartId argument even when it's called from setInterval! The setInterval doesn't even have any parameters to pass along to the cycleCharts function, so I'm very baffled as to why chartId is not undefined when cycleCharts is called from the setInterval.

setInterval is feeding cycleCharts actual timing data ( so one can work out the actual time it ran and use to produce a less stilted response, mostly practical in animation )
you want
var cycleId = setInterval(function(){ cycleCharts(); }, 10000);
( this behavior may not be standardized, so don't rely on it too heavily )

It tells you how many milliseconds late the callback is called.

var cycleId = setInterval(cycleCharts, 10000, 4242);
From the third parameter and onwards - they get passed into the function so in my example you send 4242 as the chartId. I know it might not be the answer to the question you posed, but it might the the solution to your problem? I think the value it gets is just random from whatever lies on the stack at the time of passing/calling the method.

Related

Append items ordering by placed amount

I'm using this function to append new items in order by the amount. This function is being called every 30-50ms.
var insertBefore = false;
container.find('.roll-user-row[data-user-id="' + user_data.id + '"]').remove();
container.children().each(function () {
var betContainer = $(this), itemAmount = $(this).attr('data-amount'), betId = $(this).attr('data-user-id');
if (itemAmount < betData.totalAmount) {
insertBefore = betContainer;
return false;
}
});
if (insertBefore) {
$(template).insertBefore(container);
} else {
container.prepend(template);
}
itemAmount = $(this).attr('data-amount') is integer, betData.totalAmount is interger too. And if appending goes slower than ±300ms - everything works well. In case of fast appending I get this result:
and thats not even close what I want - thats random. How to solve this?
1. Refactoring
First of all, return within .each callback doesn't work. It just breaks current iteration, not all the cycle. If you want to interrupt cylce, you should use simple for-loop and break statement. Then, I would recommend to call $() as rarely as possible, because this is expensive. So I would suggest the following refactoring for your function:
function run() {
container.find('.roll-user-row[data-user-id="' + user_data.id + '"]').remove();
var children = container.children();
for (var i = 0; i < children.length; i++) {
var betContainer = $(children[i]); // to cache children[i] wrapping
var itemAmount = betContainer.attr('data-amount');
var betId = betContainer.attr('data-user-id');
if (itemAmount < betData.totalAmount) {
$(template).insertBefore(container);
return; // instead of "break", less code for same logic
}
}
container.prepend(template); // would not be executed in case of insertBefore due to "return"
}
2. Throttling
To run a 50ms repeating process, you are using something like setInterval(run, 50). If you need to be sure, that run is done and this is 300ms delay, then you may use just setInterval(run, 300). But if the process initializes in a way that you can't change, and 50ms is fixed interval for that, then you may protect run calling by lodash throttle or jquery throttle plugin:
var throttledRun = _.throttle(run, 300); // var throttledRun = $.throttle(300, run);
setInterval(throttledRun, 50);
setInterval is just for example, you need to replace your initial run with throttled version (throttledRun) in your repeater initialization logic. This means that run would not be executed until 300ms interval has passed since the previous run execution.
I am only posting the approach here, if my understanding is right, then I'll post a code. First thing came to my mind reading this was the 'Virtual DOM' concept. Here is what you can do,
Use highly frequent random function calls only to maintain a data structure like an object. Don't rely on DOM updates.
Then use a much less frequent setInterval repetitive function call to redraw (or update) your DOM from that data structure.
I am not sure there are any reason you can't take this approach, but this will be the most efficient way to handle DOM in a time critical use-case.

Write a function that checks how many times another function will be called in a time interval

Say I have a function that logs "Hello" every 500 ms.
var logHello = function() {
setInterval(function(){
console.log("Hello");
}, 500);
};
Is there a way to write another function that will check if logHello gets called more than or equal to 1 time every second(without modifying the original logHello function).
In this case it will return true because Hello will get logged 2 times in 1 seconds.
I am assuming you want to do this for debug reasons, so I must warn you not to include this code in any production application, as it's really just meant for debugging. It's very cool that our solution works however it overwrites native javascript functionality which is typically frowned upon because it can cause code to behave differently than expected if you alter a native functions behaviour.
If it's a condition that you are not allowed to modify your code, you can simply overwrite javascript's setInterval, and use it as a "hook" into your function. We will modify setInterval to now track the time difference (seconds) inbetween calls to your method. We will then invoke and return the original setInterval method so that your code still works exactly as expected:
// keep a pointer to the original setInterval function
var oldSetInterval = window.setInterval;
// we will create our own setInterval function and put logging in it
window.setInterval = function(block, interval) {
var lastRunAt;
return oldSetInterval(function() {
// here is where we print how long it's been since the method last ran
if(lastRunAt) {
console.log("the interval last ran " + (Date.now()-lastRunAt)/1000 + " seconds ago");
}
lastRunAt = Date.now();
block();
}, interval);
}
And now running logHello() yields:
Hello
the interval last ran 0.504 seconds ago
Hello
the interval last ran 0.504 seconds ago
Hello
the interval last ran 0.505 seconds ago
This assumes you're running on the web. If you're in node, replace references to window with globals.

How to write a generic function that passes one argument to then function then called multiple times

I have been stuck on this homework:
Create a generic function that outputs one line of the countdown on
the web page, followed by an alert, and receives the data to output as
an input parameter.
Use that function to output each line of the countdown, and an alert.
Please note that you are outputting the countdown to the browser
window this time, not to an alert!
The alert is only being used to signal when to output the next line
I need help in how to come up with a generic function that passes only one argument and then can be called 13 times. To write a for loop that output the numeric part of a countdown.
I think the key here is that they're asking for "Generic".
That means one function that doesn't have to know anything but what it's doing.
It also usually means that it shouldn't remember anything about what it did last time, or what it's going to do next time it's called (unless you're writing a generic structure specifically for remembering).
Now, the wording of the specification is poor, but a generic function which:
takes (input) data to write
writes the input to the page
calls an alert
is much simpler than you might think.
var writeInputAndAlert = function (input) {
// never, ever, ***ever*** use .write / .writeLn in the real world
document.writeLn(input);
window.alert("next");
};
If I was your teacher, I would then rewrite window.alert to handle the non-generic portion.
It's non-generic, because it knows the rules of the program, and it remembers where you are, and where you're going.
var countFrom = 100,
currentCount = countFrom,
countTo = 0;
var nextCount = function () {
currentCount -= 1;
if (currentCount >= countTo) { writeInputAndAlert(currentCount); }
};
window.alert = nextCount;
edit
var countdownArray = ["ten", "nine", "eight", "Ignition Start", "Lift Off", "We have Lift Off"],
i = 0, end = countdownArray.length, text = "",
printAndAlert = function (item) {
alert();
document.write(item);
};
for (; i < end; i += 1) {
text = countdownArray[i];
printAndAlert(text);
}
This really doesn't need to be any harder than that.
printAndAlert is a generic function that takes one input, writes that input and triggers an alert.
You call it inside of a for loop, with each value in your array.
That's all there is to it.
If I understand correctly, you want to create a function that will allow you to pass the data once, and then you can call that function to output the data line by line.
To do this exactly that way isn't possible, but this method is almost the same:
function createOutputFunction(dataArray)
{
return function() {
document.write(dataArray.shift()); // This writes the first element of the dataArray to the browser
};
}
//It can then be used like this
outputFunction = createOutputFunction(["Banana", "Mango", "Apple"]);
outputFunction();
outputFunction();
outputFunction();
The "createOutputFunction" function returns a function that can read the "dataArray" variable and print its first element every time it is called.

Jquery in 30 days inquiry

I am currently watching a tutorial video "30 days to learn jQuery".
I have a question about why the tutor in the video returned a variable from a function.
Here's the code:
This is in the HTML file which just binds an event handler to buttons, calls functions, etc.
(function() {
slider.nav.find('button').on('click', function() {
slider.setCurrent( $(this).data('dir') );
slider.transition();
});
})();
and this is the one function I'm interested in (in the js file):
Slider.prototype.setCurrent = function( dir ) {
var pos = this.current;
pos += ( ~~( dir === 'next' ) || -1 );
this.current = ( pos < 0 ) ? this.imgsLen - 1 : pos % this.imgsLen;
return pos; // <== HERE
};
The only thing I want to figure out is why return pos? I tried removing it and the code still worked.
Was it a mistake or is there sound logic to this?
In a nutshell, setCurrent function is called and setCurrent returns a value. But why?
I can't speculate as to why your tutor did that, but a common practice, especially in jQuery, is to overload a function by having the same function be both a setter and getter based on what parameters are passed. An example is below:
function (dir)
{
//If dir was passed as a parameter
if (typeof dir !== "undefined")
{
//Setter code
value = dir;
}
//Getter (always returns value)
return value;
}
There are tons of functions in jQuery that use this, e.g .val, .height, .css('propertyName', [optionallySetValue]), etc.
Let me know if this makes sense, or if you have any questions :)
It's hard to know without seeing the rest of your code, but often functions that set a value on object will return something, even though that is counterintuitive, since their purpose is to set a value, not to get something.
The most common pattern you'll see is to return the object itself. The allows you to chain together multiple setter calls at once:
object.setColor("red").setSize("large");
This is not specifically going on in your case, but the author may have had a similar use in mind, in the sense that he wanted to do 2 things at once: set a value, and get some information about how that value was set.

UserScript Timer Error

Okay, so there is a button on a page that I'm trying to change the text of for a count-down done in Javascript. I'm fairly new to the language (2 days), and am not sure as to what is wrong with my code. Instead of waiting the full second before iterating again, it instantly re-iterates.
var c = 15;
function countDown(e){
if (c!=0){
e.value = 'Reply (' + c + ')';
c--;
setTimeout(countdown(e),1000);
}
else{
e.value = 'Reply'}
}
}
but it seems that instead of taking 15 seconds like I assumed, it fires off all at once (proven by me adding in an alert('a'); in the if statement I could see the button text changing)
I'm not sure if it's a problem with Greasemonkey or a problem with my javascript.
Your problem is with this line:
setTimeout(countdown(e),1000);
countdown(e) is a call to the countdown function that returns void. The setTimeout function accepts a function reference and a timeout, so you need to change it to:
setTimeout(countdown, 1000);
Your current code is calling countdown(e) 15 times recursively and then setTimeout(void, 1000);
If you need setTimeout to pass arguments (like e) to your function you can make use of the optional parameters after the timeout.
setTimeout(countdown, 1000, e);

Categories

Resources