Second function start before first is finished, Javascript - javascript

I have a for loop outside of these two big if´s. My problem seems to be that the second function seems to fire off with a short delay. Should´t First function be done and then second will run?
Can´t figure the problem out why second start before first one is finished. When second function fires aswell it will increase the orignal number from zero to 60.
if(activeSlideBoolean && activeSlide < i){
step = 0;
div[activeSlide].className = 'slide left';
animateHalfLeft(activeSlide);
function animateHalfLeft(activeSlide){
if(step < -60){
return;
}
div[activeSlide].style.left = step+'em';
step -= 6;
setTimeout (function(){ animateHalfLeft(activeSlide);},100);
}
activeSlideBoolean = false;
}
if((activeSlideBoolean === false) && (RestOfSlides)){
step = 60;
animateTotalLeft(RestOfSlides);
function animateTotalLeft(RestOfSlides){
f(step < -60){
return;
}
div[RestOfSlides].style.left = step+'em';
step -= 6;
setTimeout (function(){ animateTotalLeft(RestOfSlides);},100);
}
}

Your function animateHalfLeft and animateTotalLeft are exactly the same except for one thing the argument activeSlide and RestOfSlides.
I suggest first to create a separate function :
function animateToLeft(whatToSlide){
if(step < -60){
return;
}
div[whatToSlide].style.left = step+'em';
step -= 6;
setTimeout (function(){ animateToLeft(whatToSlide);},100);
}
}
But the function is still finishing before setTimeout ends (or even starts).
Javascript is a functional language. Use it :
function animateToLeft(whatToSlide, step, callback){
if(step < -60){
if (typeof callback === 'function') {
callback();
}
return;
}
div[whatToSlide].style.left = step+'em';
step -= 6;
setTimeout (function(){
animateToLeft(whatToSlide , step, callback);
},100);
}
}
if(activeSlideBoolean && activeSlide < i){
var isStarted = false;
animateToLeft(activeSlide, 0, function() {
if (isStarted) {
return;
}
isStarted = true;
if (RestOfSlides) {
animateToLeft(RestOfSlides, 60, null);
// Finishing order 3
}
});
// Finishing order 1
}
if(activeSlideBoolean === false && RestOfSlides){
animateToLeft(RestOfSlides, 60, null);
// finishing order 2
}
I don't know if it works for you (don't copy/paste idiotly) but for me, it's a good start.

Related

How to update a variable to use in another scope

I have a variable let second = 20 that i make 1 lower until it hits 0. When it hits 0 i want to stop running a part of my code but the variable second is always 20 when i use it because i make it lower in another scope. Sorry if my explenation is a bit unclear.
Here is the code:
votingEnd = document.querySelector(".imposters__voting");
imposters = document.querySelectorAll(".imposter");
let second = 20;
window.addEventListener("load", function () {
let myinterval;
myinterval = setInterval(function () {
second--;
if (second < 11) {
votingEnd.style.color = "red";
}
votingEnd.innerHTML = `Voting ends in: ${second}s`;
if (second == 0) {
clearInterval(myinterval);
votingEnd.innerHTML = `Voting has ended`;
}
}, 1000);
});
if (second > 0) {
//second is still 20 here because i lowered it in my function above. How can i solve this
for (let i = 0; i < imposters.length; i++) {
imposters[i].addEventListener("click", function () {
let count = 0;
while (count < imposters.length) {
imposters[count++].classList.remove("voted");
}
this.classList.add("voted");
});
}
}
You could put the if (second > 0) inside the click function that way it will check for the most recent value of second instead of just once on load like so
for(let i = 0; i < imposters.length; i++){
imposters[i].addEventListener("click", function () {
if (second > 0) {
let count = 0;
while (count < imposters.length) {
imposters[count++].classList.remove("voted");
}
this.classList.add("voted");
}
});
The problem has nothing to do with scope. It has to do with timing. That last part of your code only runs once, before the interval runs twenty times.
Here's the order of operations:
Initialize second to 20.
Bind the countdown function to window.onload. (This does not run yet)
Check if seconds is greater than 0, and it is because the intervals haven't run yet. This is the only time this code ever runs.
window.onload is triggered, and your countdown begins
one second later, seconds is now 19
19 seconds later seconds is not 0, and the interval is cleared.
So what you need to do is trigger your code in each iteration of the interval.
You want something closer to:
let second = 20;
window.addEventListener("load", function () {
const myinterval = setInterval(function () {
second--;
// other logic here...
if (second > 0) {
countdownTick(); // runs every second with the interval handler
}
if (second == 0) {
clearInterval(myinterval);
// cleanup
}
}, 1000);
});
function countdownTick() {
// Do the stuff you need to do each second here
}
Your setInterval runs every second. That does not mean that the rest of the code also runs every second. Which is why second is still 20 when the code gets to if (second > 0).
So you need to make sure that this part of your code runs every second as well. One solution would be to wrap that code inside a function which you call inside the interval, like this:
votingEnd = document.querySelector(".imposters__voting");
imposters = document.querySelectorAll(".imposter");
let second = 20;
window.addEventListener("load", function () {
let myinterval;
myinterval = setInterval(function () {
second--;
if (second < 11) {
votingEnd.style.color = "red";
}
votingEnd.innerHTML = `Voting ends in: ${second}s`;
if (second == 0) {
clearInterval(myinterval);
votingEnd.innerHTML = `Voting has ended`;
}
check();
}, 1000);
});
function check() {
if (second > 0) {
//second is still 20 here because i lowered it in my function above. How can i solve this
for (let i = 0; i < imposters.length; i++) {
imposters[i].addEventListener("click", function () {
let count = 0;
while (count < imposters.length) {
imposters[count++].classList.remove("voted");
}
this.classList.add("voted");
});
}
}
}

clearTimeout(timer) not taken into account - New timer variable created each time

In the current slideshow I'm building, I use a timer variable to rotate the slides automatically every 4s. Since manual controls are also present, I wanted to reset this timer whenever the controls are used to avoid any premature succession between two slides, but didn't manage to do it.
I supposed it to be a scope problem, but the timer variable is out of the functions that are trying to share it (showSlide(n) and changeSlide(n)). Yet a brand new timer variable seems to be created each time the changeSlide function is called : the slides automatic rotation quickens each time the "next" control is used, as if multiple timeouts were calling the function simultaneously. What is wrong here ?
const slideshows = document.getElementsByClassName("js-slideshow");
[].forEach.call(slideshows, function(slideshow) {
slideshowlize(slideshow);
});
function slideshowlize(slideshow){
const desc = slideshow.getElementsByClassName("js-desc");
const slide = slideshow.getElementsByClassName("js-slide");
let timer;
let index = 0;
const slidePrev = slideshow.querySelector('.js-prev');
const slideNext = slideshow.querySelector('.js-next');
function showSlide(n){
clearTimeout(timer); // This one is not used yet
if(n < 0){
n = slide.length -1;
}
else if(n > slide.length -1){
n = 0;
}
let i;
for(i = 0; i < slide.length; i++){
slide[i].classList.remove("is-shown");
}
for(i = 0; i < desc.length; i++){
desc[i].classList.remove("is-shown");
}
slide[n].classList.add("is-shown");
desc[n].classList.add("is-shown");
index = n;
timer = setTimeout(function(){
changeSlide(1);
}, 4000);
}
function changeSlide(n){ // this is where the magic doesn't happen
clearTimeout(timer);
if (n > 0){
showSlide(index += 1);
} else {
showSlide(index -= 1);
}
timer = setTimeout(function(){
changeSlide(1);
}, 4000);
}
showSlide(index);
slidePrev.addEventListener('click', function(){
changeSlide(-1);
});
slideNext.addEventListener('click', function(){
changeSlide(1);
});
}
Edit : Two different timers were set up. Since showSlide(n) were already resetting the timer, changeSlide(n) had no need to do it too. Thanks to Bergi for pointing it out.
function changeSlide(n){
//removed "clearTimeout(timer);"
if (n > 0){
showSlide(index += 1);
} else {
showSlide(index -= 1);
}
//removed "timer = setTimeout(...);"
}

cancel setTimeout inside an IIFE inside a loop

I have a function that will click on dom elements in order to show and hide them.
The function is working correctly and cycling through my dom elements however I am not able to stop the setTimeout once I call the function due to the IIFE I am using.
I've added a variable to the global space to break out of the loop however the setTimeout is already initiated so it never stops. I have also tried to assigne a name to the first setTimeout with a truthy/false flag in an attempt to catch and stop the next iteration of setTimeout in the loop but that has been unsuccessful as the console log continues when I attempt to stop the setTimeout.
function cycleSlides() {
var slides = [1,2,3,4]
var closeSlide = $(".close");
for(var i = 0; i < slides.length; i++) {
(function() {
var x = i;
setTimeout(function() {
if(x == 0) {
console.log("slides[x].click()")// first slide
}
else {
setTimeout(function() { console.log("closeSlide.click()") }, 1000) // timeout for animation to finish when clicked
setTimeout(function() { console.log("slides[x].click()") }, 2000) // timeout for animation to finish when clicked
}
// CLOSE LAST SLIDE
if(x == slides.length - 1 ) {
setTimeout(function() {
console.log("closeSlide.click()")
}, x * 1000)
}
}, x * 3000)
}(i))
}
}
setTimeout() returns a timeout ID that can passed to clearTimeout() to cancel the timeout. You could store the timeout IDs returned by your calls to setTimout() in an array. Then, when you want to cancel them, call clearTimeout() with the values and clear the array. Here's what that code might look like:
var timeouts = [];
function cancelTimeouts() {
for (var i = 0; i < timeouts.length; i++) {
clearTimeout(timeouts[i]);
}
timeouts = []
}
function cycleSlides() {
var slides = [1,2,3,4]
var closeSlide = $(".close");
for(var i = 0; i < slides.length; i++) {
(function() {
var x = i;
timeouts.push(setTimeout(function() {
if(x == 0) {
console.log("slides[x].click()")// first slide
}
else {
timeouts.push(setTimeout(function() { console.log("closeSlide.click()") }, 1000)) // timeout for animation to finish when clicked
timeouts.push(setTimeout(function() { console.log("slides[x].click()") }, 2000)) // timeout for animation to finish when clicked
}
// CLOSE LAST SLIDE
if(x == slides.length - 1 ) {
timeouts.push(setTimeout(function() {
console.log("closeSlide.click()")
}, x * 1000))
}
}, x * 3000))
}(i))
}
}

fail to setTimeout in a for loop

I'm building a simon game. And after each round the player should see the moves he must play in the next round. So i created a function showMoves which flashes the square he has to play. The problem is that the function is not showing anything. Can anyone tell me what did i miss?
// the effect
function flasher(index) {
$(moves[index]).fadeIn(50).fadeOut(50).fadeIn(50).fadeOut(50).fadeIn(100);
}
var interval2;
// show the moves that supposed to be played
function showMoves() {
for (var i = 0; i < moves; i++) {
if (i === 0) {
interval2 = setTimeout(flasher(i), 1000);
} else {
interval2 = setTimeout(flasher(i), (i+1) * 1000);
}
}
}
setTimeout accepts a function as a first parameter. I assume that by calling flasher you tried to avoid this situation. In you case, this should be done like this:
function showMoves() {
for (var i = 0; i < moves; i++) {
if (i === 0) {
interval2 = setTimeout(function(i) {return function() {flasher(i)}}(i), 1000);
} else {
interval2 = setTimeout(function(i) {return function() {flasher(i)}}(i), (i+1) * 1000);
}
}
}
The setTimeout and setInterval are a little diffrent than we think about them.
They are save some event on specified times that will be fired in its times. Because of this they has a problem with loops:
for(i=0;i<3;i++)
{
setTimeout(function(){alert(i)}, i*1000);
}
after ending the loop the browser has 3 jobs to do:
alert(i) after 1 second
alert(i) after 2 seconds
alert(i) after 3 seconds
But what is the value of 'i'. If you are in c# programming after ending the loop 'i' will be disposed and we have not that.
But javascript does not dispose 'i' and we have it yet. So the browser set the current value for i that is 3. because when 'i' reaches to 3 loop goes end. Therefor Your browser do this:
alert(3) after 1 second
alert(3) after 2 seconds
alert(3) after 3 seconds
That is not what we want. But if change the above code to this:
for(i=0;i<3;i++){
(function (index)
{
setTimeout(function () { alert(index); }, i * 1000);
})(i);
}
We will have:
alert(0) after 1 second
alert(1) after 2 seconds
alert(2) after 3 seconds
So as Maximus said you mast make the browser to get value of i currently in loop. in this way:
setTimeout(function(i) {return function() {flasher(i)}}(i), (i+1) * 1000);
i does not leave out until end of loop and must be get value just now.
What I can derive from your code is that moves is an array, but you're using it as if it's an integer in the for loop. And that's why nothing happens at all.
Replace:
for (var i = 0; i < moves; i++) {
With:
for (var i = 0; i < moves.length; i++) {
And you should see things happening.
But you will notice flasher is called immediately, without timeout. And that's because the result of flasher is set to be called, instead of flasher itself.
Other answers here suggest using an wrapper function, but this requires workarounds to correctly pass the index to the function called by setTimeout.
So assuming that it doesn't have to run in IE8 and below, the following is the most concise solution:
setTimeout(flasher.bind(null, i), (i+1) * 1000)
Full working example:
var moves = [1, 2, 3, 4];
function flasher(index) {
console.log('move', moves[index]);
}
var interval2;
// show the moves that supposed to be played
function showMoves() {
for (var i = 0; i < moves.length; i++) {
interval2 = setTimeout(flasher.bind(null, i), (i+1) * 1000);
}
}
showMoves()

Create a JavaScript loop without while

I am trying to create a page which needs to preform lots of loops. Using a while/for loops cause the page to hang until the loop completes and it is possible in this case that the loop could be running for hours. I have also tried using setTimeout, but that hits a recursion limit. How do I prevent the page from reaching a recursion limit?
var looper = {
characters: 'abcdefghijklmnopqrstuvwxyz',
current: [0],
target: '',
max: 25,
setHash: function(hash) {
this.target = hash;
this.max = this.characters.length;
},
getString: function() {
string = '';
for (letter in this.current) {
string += this.characters[this.current[letter]];
}
return string;
},
hash: function() {
return Sha1.hash(this.getString());
},
increment: function() {
this.current[0] += 1;
if (this.current[0] > this.max) {
if (this.current.length == 1) {
this.current = [0, 0];
} else {
this.current[1] += 1;
this.current[0] = 0;
}
}
if (this.current[1] > this.max) {
if (this.current.length == 2) {
this.current[2] == 0;
} else {
this.current[3] += 1;
this.current[2] = 0;
}
}
},
loop: function() {
if (this.hash() == this.target) {
alert(this.getString());
} else {
this.increment();
setTimeout(this.loop(), 1);
}
}
}
setInterval is the usual way, but you could also try web workers, which would be a more straightforward refactoring of your code than setInterval but would only work on HTML5 browsers.
http://dev.w3.org/html5/workers/
Your setTimeout is not doing what you think it's doing. Here's what it's doing:
It encounters this statement:
setTimeout(this.loop(), 1);
It evaluates the first argument to setTimeout, this.loop(). It calls loop right there; it does not wait for a millisecond as you likely expected.
It calls setTimeout like this:
setTimeout(undefined, 1);
In theory, anyway. In reality, the second step never completes; it recurses indefinitely. What you need to do is pass a reference to the function rather than the returned value of the function:
setTimeout(this.loop, 1);
However, then this will be window on the next loop, not looper. Bind it, instead:
setTimeout(this.loop.bind(this), 1);
setInterval might work. It calls a function every certain amount of milliseconds.
For Example
myInterval = setInterval(myFunction,5000);
That will call your function (myFunction) every 5 seconds.
why not have a loop checker using setInterval?
var loopWorking = false;
function looper(){
loopWorking = true;
//Do stuff
loopWorking = false;
}
function checkLooper()
{
if(loopWorking == false)
looper();
}
setInterval(checkLooper, 100); //every 100ms or lower. Can reduce down to 1ms
If you want to avoid recursion then don't call this.loop() from inside of this.loop(). Instead use window.setInterval() to call the loop repeatedly.
I had to hand-code continuation passing style in google-code prettify.
Basically I turned
for (var i = 0, n = arr.length; i < n; ++i) {
processItem(i);
}
done();
into
var i = 0, n = arr.length;
function work() {
var t0 = +new Date;
while (i < n) {
processItem(i);
++i;
if (new Date - t0 > 100) {
setTimeout(work, 250);
return;
}
}
done();
}
work();
which doesn't hit any recursion limit since there are no recursive function calls.

Categories