Run one for loop iteration every 200 msecs

I don’t find a setTimeout or setInterval scenario that works for me. w3c schools explanations not clear to me. What do I need to do to make each of the 5 iterations in the example below execute 200 msec apart?
(I also cannot seem to get the code to print here, hence the ** at the beginning of every line)
Thanks

<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag. </canvas>

<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
tx.fillStyle = "rgba(0,200,0,1)";
var i;
var x = 36;
var y = 10;
var r = 5;
// want to iterate for-loop every 200 msec
for (i=0; i < 5; i++) {
  tx.arc(x, y, r, 0, 2 * Math.PI);
  tx.fill();
  x = x+10;
  y = y+5;
} // end for
</script>

I’ve done my level best to try and get your code back in a fit state for presentation. You just need to use three backticks ( ``` ) - usually the key at the left-hand end of the number row of the keyboard. - on the line before and the line after your code.

You might want to just check how the code looks and edit if necessary.

2 Likes

Thank you.

The setInterval (and setTimeout) functions take a function, and a time delay. These functions won’t be precisely at 200 milliseconds, as the browser and computer can have some delays, but they’ll get to it pretty close to on time.

A simple way is to place a function within setInterval:

setInterval(function animate() {
  for (i=0; i < 5; i++) {
    tx.arc(x, y, r, 0, 2 * Math.PI);
    tx.fill();
    x = x+10;
    y = y+5;
  } // end for
}, 200);

However, this separates the 200 millisecond section from the setInterval, making it difficult to easily understand.

As such, a better technique is to place the function outside, and then refer to it from the setInterval:

function animate() {
  for (i=0; i < 5; i++) {
    tx.arc(x, y, r, 0, 2 * Math.PI);
    tx.fill();
    x = x+10;
    y = y+5;
  } // end for
}
setInterval(animate, 200);

More complex solutions can be done using window.requestAnimationFrame(), where you keep track of the time manually to achieve something closer to proper time, but such techniques are best put off until their added complexity demands using them.

Or do you want a delay between each of the iterations of the loop? In this case might put the loop body into the callback function of setInterval, and clear the interval when a count variable reaches 5. Like e.g.

// ...
var count = 0;
var interval = window.setInterval(loop, 200);

function loop() {

  tx.arc(x, y, r, 0, 2 * Math.PI);
  tx.fill();
  x = x + 10;
  y = y + 5;
  
  if (++count === 5) {
    window.clearInterval(interval);
  }
}

Son of a gun…
m3g4p0p is apparently what I am looking for. it does pause between each pass. But it doesn’t stop after 5. Maybe because I am trying to test it as a standalone. Will keep trying. (As an old mainframe programmer, I find this concept entirely counter intuitive.)

Sitepoint says this is a duplicate post. Frustration level is through the roof. If it is a duplicate my apologies. Following code show dot exactly once. Had to alter interface to add the arguments of course.

<!DOCTYPE html>
<html>
<head>
    <style type="text/css">
        canvas{border:#666 1px solid;}    
    </style>
    <script type="application/javascript" language="javascript">
    function showSpots(ctx,x,y,r,i,interval){
        ctx.arc(x, y, r, 0, 2 * Math.PI);
        ctx.fill();
        x = x + 10;
        y = y + 5;
        if (++i === 5) {
            window.clearInterval(interval);
            alert("clear");
        } // end if
    } // end showSpots
    
    function draw(){
        alert("draw");
        var canvas = document.getElementById('canvas'); // Assign canvas to a variable
        var ctx = canvas.getContext('2d');              // Create HTML5 context object
        var x = 36;
        var y = 10;
        var r = 5;
        ctx.fillStyle = "rgba(0,200,0,1)";              // fillStyle (r,g,b,alpha);
        var i = 0;
        var interval = window.setInterval(showSpots(ctx, x,y,r,i),200);
    } // end draw
    </script>
</head>
<body onload="draw();">
    <canvas id="canvas" width="640" height="360"></canvas>
</body>
</html>

[quote=“Grnadpa, post:8, topic:230220, full:true”]
Following code show dot exactly once. Had to alter interface to add the arguments of course.[/quote]

Okay, let’s follow things through to find out what’s happening.

var interval = window.setInterval(showSpots(ctx, x,y,r,i),200);

setInterval is assigned the return value of the showSpots function. The showSpots function shows one spot, as it’s supposed to, and returns nothing. That undefined return is what gets given to the setInterval function.

That is different from what you want to achieve. What you want to achieve is to have a function assigned to setInterval, so that it is that function (which we’ll call a wrapper) that gets called every 200 milliseconds.

That wrapper function will then call showSpots, with the appropriate arguments, so that wrapper function will also need access to those variables. Because setInterval is called at some later time, there is no access to those previously defined variables, unless we involve horrible globals. What can we do instead to give the wrapper function access to those variables?

Closure, is a technique that lets us give a function access to local variables. When a function is created, it retains access to the local scope. For example:

function wrapper(foo) {
    var bar = "bar";
    return function closure() {
        console.log(foo, bar);
    };
}
var func = wrapper("foo");
func(); // "foo", "bar"

The closure function gets assigned to the func variable. That closure function (in the guise of func) retains access to the function arguments, and other variables within that scope. See this closure documentation for more.

What we want, is to give that closure function to setInterval, so that setInterval will run that closure function and the closure function can then call showSpots for us.

We are then faced with another problem where the updated variables for x, y, and i are not being updated. This is because they are function arguments that have been passed by value. Only if they are an object that has been passed by reference will such changes remain in place.

There are two different ways to deal with this. One is to place the variables that will change in to a object, so that you can retrieve the values and change the values in that object. Another solution is to not pass the variables to the function so that the function will instead find those variables from the outer scope. That second solution seems to be a good fit for what’s happening here.

Lastly, the interval variable cannot be found, so we’ll place it as a property on the ctx object instead.

We end up with the following code:

function showSpotsWrapper(ctx, x, y, r) {
    var i = 0;
    return function showSpots() {
        ctx.arc(x, y, r, 0, 2 * Math.PI);
        ctx.fill();
        x = x + 10;
        y = y + 5;
        i += 1;
        if (i === 5) {
            window.clearInterval(ctx.interval);
        }
    };
}
...
ctx.interval = window.setInterval(showSpotsWrapper(ctx, x, y, r), 200);

And in case you’re interested in the technique where you pass an object, that would work like this:

function showSpots(spotInfo) {
    spotInfo.ctx.arc(spotInfo.x, spotInfo.y, spotInfo.r, 0, 2 * Math.PI);
    spotInfo.ctx.fill();
    spotInfo.x += 10;
    spotInfo.y += 5;
    spotInfo.i += 1;
    if (spotInfo.i === 5) {
        window.clearInterval(spotInfo.ctx.interval);
    }
}
function showSpotsWrapper(ctx, x, y, r) {
    var spotInfo = {ctx: ctx, x: x, y: y, r: r, i: 0};
    return function closure() {
        showSpots(spotInfo);
    };
}
...
ctx.interval = window.setInterval(showSpotsWrapper(ctx, x, y, r), 200);

Thank you for your kind attention and explanation. I’m going to have to read it over ten or twelve times. There clearly is a javascript-way-of-thinking that I don’t have yet. (Keep good thoughts for my eventual epiphany.)

Will give it another shot tomorrow.

Thanks again.

1 Like

Spent quite a bit of time studying your reply – both before replacing the code in my program and after. Still a little foggy on Closure(), particularly after reading the reference you provided. So if you don’t mind …

I tend to avoid “horrible globals” despite my mainframe programming background. Still, would you share how Closure() is a superior solution to globals in this case?

Your preferred solution, and the one I used, doesn’t involve Closure(). So I’m appreciative, but confused as to why you included the Closure() discussion.

I certainly would not have figured this out on my own, no matter how many Google examples.

I cannot begin to share my great appreciation for the kindness and thoroughness of your replies.

Once you wrap all of your JavaScript inside an IIFE (immediately invoked function expression) so that the variables are taken out of the global namespace then you will have created a closure as those variables will continue to exist after the IIFE terminates as they are still used by the code in the setInterval

[quote=“Grnadpa, post:11, topic:230220, full:true”]
Your preferred solution, and the one I used, doesn’t involve Closure(). So I’m appreciative, but confused as to why you included the Closure() discussion.[/quote]

The name of that function in the closure discussion could have been anything, and I should have called it something else to avoid confusion.

Closure is the name of a technique where a function that is returned from another function retains access to the execution context of it’s parent.

When a variable is defined, the scope of the variable can be global scope or local scope. Global scope is where the variable is defined outside of a function, and local scope is where it’s defined inside of a function.

Let’s use some of the code from before as an example, to give us something concrete to talk about.

function showSpotsWrapper(ctx, x, y, r) {
    var i = 0;
    return function showSpots() {
        ctx.arc(x, y, r, 0, 2 * Math.PI);
        ...
    };
}

The function arguments for showSpotsWrapper along with the i variable are accessible from within the showSpotsWrapper function. Any functions that are defined in there also have access to those variables, as they are a part of the execution context for that function.

Execution context is the environment within which the code is executed, which includes any local scope variables. When functions are defined, they retain access to the execution context from which they were created.

If we were to invoke the showSpotsWrapper function, with:

var wrappedShowSpots = showSpotsWrapper(ctx, x, y, r);

The showSpotsWrapper function from the above code returns the showSpots function, which is assigned to the wrappedShowSpots variable so that we can gain further access to it. The showSpots function (currently accessible via the wrappedShowSpots variable), retains the execution context from where it was defined.

Is the case of your code, instead of being assigned to a variable it is being given to the setInterval function as a function argument. Whenever setInterval invokes that function, the same rules apply.

So now that we are outside of the showSpotsWrapper function, the showSpots function that was returned from it still retains access to the variables that were in the showSpotsWrapper function. Each time we invoke the wrappedShowSpots function, it can access and manipulate things like the i variable, that were in the showSpotsFunction.

Here’s a good article on Understanding Scope and Context in JavaScript where it guides you through ideas of scope, context, the scope chain, and closures.

In an easier to absorb manner, Closures in JavaScript has a good overview of closures, along with a useful venn diagram:


But for me, the better diagram is found in the JavaScript Closure Under the Hood article.


We can now visualize that the execution context of function y is the yellow local scope of function x. Function y retains its execution context when it is returned, and thus still retains access to the yellow local scope of function x. Closure is the name of that technique.

And lastly, the Jibbering FAQ has some excellent details on Closures but the content gets quite dense, so having an understanding of things from the previously-linked articles can be a good help before you dive down in to more complete details of how JavaScript does its thing.

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.