I have a list of files that I want to plot in order (line graph), creating an animation as d3 transitions between them.
I have an update function that takes as input a file name, and transitions the current line graph to the new line graph. This is working fine.
However, I now want to transition in order between five different files.
Here is the code I am using:
var file_list = ["time1.csv", "time2.csv", "time3.csv", "time4.csv", "time5.csv"];
var num_files = file_list.length;
for (i = 0; i < num_files; ++i) {
setTimeout(setDelay(i), 1000);
}
function setDelay(i) {
setTimeout(function(){
update(file_list[i]);
}, 1000);
}
This does transition my line graph, but it transitions from the first (time1.csv) immediately to the last (time5.csv), skipping everything in the middle.
How can I figure out what is going wrong?
If I console.log(file_list[i]) in the loop, it is looping through and printing time1.csv ... time5.csv.
Thanks!
The setDelay is called on every iteration, to evaluate it on timeout you must pass the function object as callback setTimeout(function() { setDelay(i); }, 1000)
But your logic is wrong, why two setTimeout per iteration? All of them is calling immediatly after each iteration, you must chain the setDelay calls or multiply the delay by iteration index.
var file_list = ["time1.csv", "time2.csv", "time3.csv", "time4.csv", "time5.csv"];
function setDelay() {
var file = file_list.shift();
if (file) {
update(file);
setTimeout(setDelay, 1000);
}
}
setDelay();
Related
When I activate showMoves() the setInterval is supposed to repeat the function one second at a time.
However, it speeds up after a few seconds and activates the function several times in a single second instead of once. Also the clearInterval isn't working even though the if statement for it turns true.
let i = -1;
function showMoves() {
const start = setInterval(showMoves, 1000);
if (i > game.computerMoves.length) {
clearInterval(start);
}
console.log(i + ' ' + game.computerMoves.length);
const showColors = new Map([
[green, 'lime'],
[yellow, 'rgb(255,255,102)'],
[blue, 'dodgerblue'],
[red, 'salmon'],
]);
i++;
let move = game.computerMoves[i];
move.style.backgroundColor = showColors.get(move);
}
You shouldn't be calling setInterval inside the very same function that's being called by setInterval -- you're setting up multiple interval timers that way, not just one that you can clear away easily. It's "speeding up" as you say because you're seeing more and more different interval timers calling the same function.
You're recursively calling showMoves - every second, a new interval is created. When the function runs and the stop condition isn't reached, the start reference you have to the interval you just created is just garbage collected. You want a method to save the intervals so that you can reference them later so they can be cleared. For example:
let intervals = [];
function showMoves() {
intervals.push(setInterval(showMoves, 1000));
if (i > game.computerMoves.length) {
// clear all currently running intervals:
intervals.forEach(clearInterval);
intervals = [];
}
// ...
I have a function triggerWave() which makes the points on the canvas animate in the wave form. I am using d3.ease('quad-in') for easing and I would like to use d3.timer() to make the triggerWave() function call over 200ms timeframe. I am out of luck in finding the tutorials or examples on d3.timer.
triggerWave() {
//function logic
let count = 0;
let xScale = d3.scale.linear().range([1,2]); // want the value to change from 1 to 2.
let zScale = d3.scale.linear().domain([0, 200]); // 200 ms.
let value = xScale(d3.ease('quad-in')(zScale(count)));
if(count < 200){
count++;
d3.timer(() => triggerWave());
} else {
// do something
}
this.wave.next({currentFrame: value});
}
When I call d3.timer() as above, the triggerWave() function gets called infinite times and never stops. I want to manipulate or control the time. In my case, I want the timer() to be triggered for 200ms.
How can I understand how to use the d3.timer() function?
(EDIT: I totally and completely missed the huge, big "V3" which is right there, in the title of the question. Sorry. I'll keep this answer here as reference for v4 users)
Since you are calling triggerWave inside the triggerWave function itself, you don't need d3.timer, but d3.timeout instead. According to the API, d3.timeout:
Like timer, except the timer automatically stops on its first callback. A suitable replacement for setTimeout that is guaranteed to not run in the background. The callback is passed the elapsed time.
Also, pay attention to the fact that you are reseting count every time the function runs, which will not work. Set its initial value outside the function.
Here is a demo with those changes. I'm calling the function every 200 ms, until count gets to 50:
var p = d3.select("p")
var count = 0;
triggerWave();
function triggerWave() {
p.html("Count is " + count)
if (count < 50) {
count++;
d3.timeout(triggerWave, 200)
} else {
return
}
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<p></p>
You can also keep track of the total elapsed time, using the argument passed to triggerWave by d3.timeout:
var p = d3.select("p")
var count = 0;
var elapsed = 0;
var format = d3.format(".2")
triggerWave();
function triggerWave(t) {
elapsed = t ? elapsed + t : elapsed;
p.html("Count is " + count + ", and the elapsed time is " + format(elapsed/1000) + " seconds")
if (count < 50) {
count++;
d3.timeout(triggerWave, 200)
} else {
return
}
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<p></p>
Since you are using D3 v3, and as there is no d3.timeout in v3, you can do the same approach using vanilla JavaScript: setTimeout.
Here is a demo:
var p = d3.select("p")
var count = 0;
triggerWave();
function triggerWave() {
p.html("Count is " + count)
if (count < 50) {
count++;
setTimeout(triggerWave, 200)
} else {
return
}
}
<script src="https://d3js.org/d3.v3.min.js"></script>
<p></p>
In version 3, there is no d3.timer.stop() function. You have to return true after a certain period of time to stop the timer.
In Gerardo's answer, he explained fabulously how to use the d3 timeout which would be a valid solution to your problem i.e., to call the function over and over for a certain period of time. But looking at your comments on Gerardo's answer, I think you are looking for something else.
Here's what I came up with and I think this is what you are looking for:
You can create an another function called as activateTriggerWave() which will be invoked on the button click and inside this function, you can call your triggerWave() method using the d3 timer.
function activateTriggerWave() {
d3.timer(elapsed => {
this.triggerWave();
if(elapsed >= 200){
return true; // this will stop the d3 timer.
}
});
}
triggerWave() {
// here you can do whatever logic you want to implement.
}
I hope this helps.
I use d3.js v3, and the timer can be stopped by any user action. In the d3.js docs it is shown to use it as:
d3.timer(function(elapsed) {
console.log(elapsed);
return elapsed >= 1000;
});
I have few examples in which the animation is forever and there is no reason to set a limit on it. Checking the standalone d3.timer which comes with a stop(), I found that it behaves quite slow comparing it with the default timer included in the v3 toolset, probably for some version incompatibility.
The solution is use is to:
var timer_1_stop=false;
set it as global var accesible from the page scope. Then the timer:
const run=function(){
//...
d3.timer(function() {
voronoi = d3.geom.voronoi(points).map(function(cell) { return bounds.clip(cell); });
path.attr("d", function(point, i) { return line(resample(voronoi[i])); });
return timer_1_stop;
});
}
const stopVoro=function(){
timer_1_stop=true;
}
It allows to do:
class Menu extends React.Component {
render(){
return(<ul>
<li><span onClick={()=>{stopVoro()}}>StopVoro</span></li>
</ul>)
}
}
This is originally from (Pause execution in while loop locks browser (updated with fiddles))
I have been at this all day and I can't figure out how to keep javascript from advancing to the next line and in essence executing all lines at once. I have tried every combination of delay / setTimeout I can think of to no avail.
I just want the elements in the array to flash once then pause, then do it again for another element in the array till all elements have been removed and the array is empty.
But because javascript is executing all lines at once I end up with the appearance of all elements flashing at the same time.
Here is the fiddle:
http://jsfiddle.net/ramjet/xgz52/7/
and the relevant code:
FlashElement: function () {
while (elementArray.length) {
alert('a ' + elementArray.length);
var $el = elementArray.eq(Math.floor(Math.random() * elementArray.length));
PageLoadAnimation.FlashBlast($el);
alert('delay complete');
elementArray = elementArray.not($el);
alert('array popped');
alert('z ' + elementArray.length);
}
},
ANSWER FOR THIS SITUATION. Hopefully it will help others.
As Zach Saucier points out the loop was really my problem...but not the only problem. I was the other problem(s).
Me first.
Fool that I am I was really causing my own complications with two things I was doing wrong.
First using jsfiddle my javascript would error due to syntax or some such thing but fiddle doesn't tell you that (to my knowledge) so my fiddle wouldn't run but I took it in pride as MY CODE IS FINE stupid javascript isn't working.
Second I was passing my function to setTimeout incorrectly. I was adding the function parens () and that is not correct either which would bring me back to issue one above.
WRONG: intervalTimer = setInterval(MyFunction(), 1500);
RIGHT: intervalTimer = setInterval(MyFunction, 1500);
As for the code. As Zach pointed out and I read here (http://javascript.info/tutorial/settimeout-setinterval) while he was responding setting a timeout in a loop is bad. The loop will iterate rapidly and with the timeout one of the steps in the loop we get into a circular firing squad.
Here is my implementation:
I created a couple variables but didn't want them polluting the global scope so I created them within the custom domain. One to hold the array of elements the other the handle to the setInterval object.
var PageLoadAnimation =
{
elementArray: null,
intervalTimer: null,
....
}
In my onReady function (the one the page calls to kick things off) I set my domain array variable and set the interval saving the handle for use later. Note that the interval timer is how long I want between images flashes.
onReady: function ()
{
elementArray = $('#PartialsContainer').children();
//black everything out just to be sure
PageLoadAnimation.BlackOutElements();
//flash & show
intervalTimer = setInterval(PageLoadAnimation.FlashElement, 1500);
},
Now instead of looping through the array I am executing a function at certain intervals and just tracking how many elements are left in the array to be flashed. Once there are zero elements in the array I kill the interval execution.
FlashElement: function ()
{
if(elementArray.length > 0) //check how many elements left to be flashed
{
var $el = PageLoadAnimation.GrabElement(); //get random element
PageLoadAnimation.FlashBlast($el); //flash it
PageLoadAnimation.RemoveElement($el); //remove that element
}
else
{
//done clear timer
clearInterval(intervalTimer);
intervalTimer = null;
}
},
So the whole thing is:
var PageLoadAnimation =
{
elementArray: null,
intervalTimer: null,
onReady: function () {
elementArray = $('#PartialsContainer').children();
//black everything out just to be sure
PageLoadAnimation.BlackOutElements();
//flash & show
intervalTimer = setInterval(PageLoadAnimation.FlashElement, 1500);
//NOT this PageLoadAnimation.FlashElement()
},
BlackOutElements: function () {
$('#PartialsContainer').children().hide();
},
FlashElement: function ()
{
if(elementArray.length > 0)
{
var $el = PageLoadAnimation.GrabElement();
PageLoadAnimation.FlashBlast($el);
PageLoadAnimation.RemoveElement($el);
}
else
{
//done clear timer
clearInterval(intervalTimer);
intervalTimer = null;
}
},
GrabElement: function()
{
return elementArray.eq(Math.floor(Math.random() * elementArray.length));
},
RemoveElement: function($el)
{ elementArray = elementArray.not($el); },
FlashBlast: function ($el) {
//flash background
$el.fadeIn(100, function () { $el.fadeOut(100) });
}
}
Hope that help others understand the way to go about pausing execution in javascript.
The reason why you were having trouble is because setTimeout function is non-blocking and will return immediately. Therefore the loop will iterate very quickly, initiating each of the timeouts within milliseconds of each other instead of including the previous one's delay
As a result, you need to create a custom function that will wait on the setInterval to finish before running again
FlashElement: function () { // Call it where you had the function originally
myLoop();
},
...
function myLoop() {
setTimeout(function () { // call a setTimeout when the loop is called
var $el = elementArray.eq(Math.floor(Math.random() * elementArray.length));
PageLoadAnimation.FlashBlast($el);
elementArray = elementArray.not($el);
if (0 < elementArray.length) { // if the counter < length, call the loop function
myLoop();
}
}, 1000)
}
Feel free to change the delay to whatever value you wish (3000ms to let each fade finish before the last at the moment). If you want to start the fade in of the next before the previous ends and keep them in their original positions you would have to animate the opacity using .css instead of using fadeIn and fadeOut
My answer is based on this answer from another SO question
I'm sure this must be a common problem, but after much searching I can't find an answer.
I have a twitter-bootstrap loading bar that I would like to update after each stage of a calculation is completed.
Here is the function for updating the loading bar:
var lb = $('#loading-bar');
var lbc = 0;
function increment_loading_bar(pc) {
setTimeout(function(){
lbc = lbc + pc;
lb.width(lbc+"%");;
}, 1);
}
And the calls to update the bar are within a .each() loop
var inc = 100/array.length();
$.each(array,function(index,element){
increment_loading_bar(inc/2);
//
//Gnarly processing ....
//
increment_loading_bar(inc/2);
}
However, this only updates after all the processing has finished. How can the redraw of the bar be forced as the code is executed?
Many thanks!
As I said in my question, the /redraw/ needs to be forced as the code is executed
To my knowledge, you can only indirectly force the Redraw by pausing your Process once in a while.
For example like this:
var inc = 100/array.length();
var processQueue = array;
var currentIndex;
setTimeout(runProcess, 5);
function runProcess() {
var element = processQueue[currentIndex];
// Process the element here
// ....
increment_loading_bar(inc);
currentIndex++;
if (currentIndex < processQueue.length) {
setTimeout(runProcess, 5);
} else {
// processing has finished
}
}
This way you give the browser some time (5ms in this example) between each step to redraw the loading bar.
Let's say there is a set of Watchers that need to be refreshed periodically. They each may have a different refresh interval. There may be several hundred such Watcher items at any given moment. The refresh time for any Watcher can range from a second to several minutes or hours.
Which is better?
Use a separate setTimeout for each one.
Use a setInterval that runs a function every second. The functions then cycles through each Watcher checking to see if it needs to be refreshed.
At first I assumed that the native code implementation of setTimeout would be more efficient than a JS function that does checking, but it's really a question of how setTimeout is implemented, how much overhead each timeout takes on a per-tick basis, and how well the number of timeouts scales.
I'm asking this for a Node application so the specific engine I'm referring to is V8, but it'd be cool if anyone knows the details for other engines as well.
Here's one idea that should be pretty efficient regardless of how setTimeout or setInterval is implemented. If you have N events scheduled for N different times in the future, create an array of objects where each object has a property for the time that the event is due and a property that tells you what type of event it is (a callback or some other identifier). Initially sort that array by the time property so the next time is at the front of the event and the furthest time is at the end.
Then, look at the front of the array, calc the time until that event and do setTimeout() for that duration. When the setTimeout() fires, look at the start of the array and process all events who's time has been reached. If, after processing an event, you need to schedule it's next occurrence, calc the time in the future when it should fire and walk the array from start to finish until you find an event that is after it and insert this one right before that event (to keep the array in sorted order). If none is found, insert it at the end. After processing all events from the front of the array who's time has been reached, calc the delta time to the event at the front of the array and issue a new setTimeout() for that interval.
Here's some pseudo-code:
function orderedQueue() {
this.list = [];
}
orderedQueue.prototype = {
add: function(time, callback) {
var item = {}, added = false;
item.time = time;
item.cb = callback;
for (var i = this.list.length - 1; i >= 0; i--) {
if (time > this.list[i].time) {
// insert after the i item
this.list.splice(i + 1, 0, item);
added = true;
break;
}
}
// if no item was after this item,
// then put this on the front of the array
if (!added) {
this.list.unshift(item);
}
},
addDelta(delta, callback) {
var now = new Date().getTime();
this.add(now + delta, callback);
},
waitNext: function() {
// assumes this.list is properly sorted by time
var now = new Date().getTime();
var self = this;
if (this.list.length > 0) {
// set a timer for the first item in the list
setTimeout(function() {
self.process();
}, this.list[0].time - now);
}
},
process: function() {
var now,item;
// call all callbacks who's time has been reached
while (this.list.length) {
now = new Date().getTime();
if (this.list[0].time <= now) {
// remove front item from the list
item = this.list.shift();
// call the callback and pass it the queue
item.cb(this);
} else {
break;
}
}
// schedule the next item
this.waitNext();
}
}
And, here's generally how you would use it:
var q = new orderedQueue();
// put initial events in the queue
q.addDelta(100, f1);
q.addDelta(1000, f2);
q.addDelta(5000, f3);
q.addDelta(10000, f4);
q.addDelta(200, f5);
q.addDelta(100, f1);
q.addDelta(500, f1);
// start processing of queue events
q.waitNext();