1

With this code i want to create a loop that will work in decreasing way. I want to use for loop going from 10 till 1. I did this, but i have an error. The error is next: after pushing on the button the last values have a delay.

Probably this is happening because the last indexes are higher at the end, and when the app divide 1000 / 2, the timeout is higher. Am i right? and how to solve the issue?

const btn = document.getElementById('btn');
btn.addEventListener('click', function(){
  
    for (let index = 10; index > 0; index--) {
        setTimeout(() => {
            console.log(index)
        },  1000 / index);      
    }
})
<div class="app">
  <button id="btn">click</button>
</div>

7
  • 2
    I'm not quite sure what you're asking, it seems to be working as expected for me. 1 goes off after 1000/1=1000 milliseconds, 2 goes off after 1000/2=500 milliseconds, and so on. What exactly is your expected result? Commented Jan 15, 2020 at 18:43
  • 1
    Just write out, what 1000 / index is, for index in [1, 10]. Long story short, y = 1/x is not linear. Commented Jan 15, 2020 at 18:44
  • Could also just write the for loop like this: for (let i = 10; --i;) { }. Is the delay something you want? What is the result you want? Please be more clear. Commented Jan 15, 2020 at 18:44
  • 1
    @StephenMIrving The question stays the same: How does that (renaming a variable) help at all? Commented Jan 15, 2020 at 18:45
  • @John Montgomery, the expected result is next: i want to decrease 10 till 1 and the interval between each iteration should be equal with 1000 sec, for example. Commented Jan 15, 2020 at 18:46

2 Answers 2

3

If you want an equal interval, your loop is fine, you just need to fix the math for calculating the timeout:

const btn = document.getElementById('btn');
btn.addEventListener('click', function(){
  
    for (let index = 10; index > 0; index--) {
        setTimeout(() => {
            console.log(index)
        },  1000*(10-index));      
    }
})
<div class="app">
  <button id="btn">click</button>
</div>

That will count down from 10 to 1 once every second, just change the 1000 as needed if you want it to be faster or slower.

Note that the order of the loop doesn't actually matter here (aside from a few ms difference in execution time) since it's asynchronous - you could use a non-reversed loop and still get the same result to the human eye.

Sign up to request clarification or add additional context in comments.

5 Comments

my question is, im i right if i will say that in each iteration the calculation will look like: 1000 * (10 - 10) 1000 * (10 - 9) 1000 * (10 - 8) 1000 * (10 - 7) ... 1000 * (10 - 1) ? The second question is, why the delay is equal everytime with 1000s if the claculations are different in each iteration?
@AskMen Because setTimeout is an asynchronous function, which means that all of them start counting at essentially the same time. So 10 gets printed 1000 * (10 - 10) = 0 ms from now, 9 gets printed 1000 * (10 - 9) = 1000 ms from now, 8 gets printed 1000 * (10 - 8) = 2000 ms from now, etc.
@AskMen The only thing I would add to John's great answer is that you should wrap your setTimeouts that are within loops in IIFEs. More info on that can be found in this article, "Understanding setTimeout Inside For Loop in JavaScript".
@StephenMIrving As your link says, that isn't needed if you use let (which you should do in general, there's no reason to use var in modern code unless you're trying to be overly clever with hoisting or something like that).
Wow, I don't know how I missed that part of it. Thanks for pointing that out to me John, I wasn't aware of that. I guess I've just always been wrapping them in IIFEs for so long, and when I started using let I didn't realize that meant I no longer had to do that.
1

If you use promises and async/await you can avoid the setTimeout overload and have precise control. The difference between this and John Montgomery's answer is this doesn't care how many times you loop. Do 10000 if you want, you won't get 10000 setTimeouts running at the same time.

const btn = document.getElementById('btn');
const wait = time=>new Promise(resolve=>setTimeout(resolve,time))
btn.addEventListener('click', async function(){      
    for (let index = 10; index > 0; index--) {
        await wait(1000)
        console.log(index)
    }
})
<div class="app">
  <button id="btn">click</button>
</div>

Async functions allow you to use the await keyword. This is a neat new feature in javascript that let's you write async code (like setTimout) as if it was sync.

const wait = time=>new Promise(resolve=>setTimeout(resolve,time)) is a fairly common helper function developers keep around for times you need to just wait some amount of time and then do something. By await'ing the wait(1000) function, I hang the execution of the loop until the time is done.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.