PencilCoder

Teacher's Guide: Timers (forever!)

Overview

This activity introduces the Pencil Code forever function, which executes a callback repeatedly, with a fixed time delay between each call. The lesson revisits the concepts of queue-based and frame-based animations, and through this discussion explores various nuances of forever and the built-in javascript setInterval method on which it is based.

More about the lesson

The Pencil Code functions forever and stop facilitate setting up and removing timers to execute callbacks at a specified rate. As the lesson illustrates, the mechanics and syntax are straightforward. Multiple timers, with potentially differing rates of execution, are permitted. Unless we specify otherwise, the timer will invoke the callback 30 times per second, or roughly every 33 milliseconds. As illustrated in the lesson, we can alter this frequency by specifying the optional first argument, which the Pencil Code document describes as frames per second (fps).

The stop method is used to remove timers set up with forever. Within the callback associated with that timer, the call stop() (with no arguments) removes that specific timer. Outside of a timer's callback, calls to stop() remove all timers created with forever. The lesson's coding snippet also illustrate that, outside of its own callback, a specific timer can be removed by referencing the unique numeric id associated with that timer—a value returned by forever when the timer is set up.

Note that calls to stop don't interrupt a currently-executing callback. Rather, it removes the timer with which that callback is associated, preventing subsqeuent calls of that callback. (As with any function, to immediately exit the callback, one would need to call return.)

To students already familiar with the concept of functions and callbacks, the mechanics of setting up and removing timers is straightforward. The challenging aspects of this lesson is working with them effectively. The main stumbling block has to do with the relationship between timing events and animation in JavaScript. In short, JavaScript animation is inextricably linked to timers. For ease of exposition, it helps to conceptualize animation logic as taking two forms: queue-based and frame based. As we shall discuss shortly, not only can you have a mix of the two; but in fact all animation is, at root, frame based.

Lessons in this course and the vast majority of associated coding activities have heretofore been based on queue-based animation. The logic of this approach to animation was described in some depth in the notes to the Array Destructuring! lesson. In the simplest case, a program using queue-based animation runs immediately, in fractions of a second; but animations arising from that code are scheduled to be carried out in the future, long after the program itself completed its execution. This process iof scheduling animations is highly complicated. Thankfully, jQuery and Pencil Code handle most of that heavy lifting for us(((; though, even with this support, when working with queue-based animations, various timing issues naturually arise. Students have learned to address these using appropriate calls to sync, await done defer(), and most recently, plan and done))).

When we instruct a script to run at speed Infinity, Pencil Code switches from queue-based animation to frame-based animation. Students have heretofore explored this feature in a handful of activities, but not many details have been provided. What they hopefully have learned is that, in this alternative approach, in its simplest form, animations are displayed without delay and as fast as the computer can manage.

In a frame-based script, there is no scheduling of animations to be carried out in the future; at least, that is the default behavior. Timing events, the focus of this lesson, let us modify that default behavior. That is, they allow us to schedule blocks of code—e.g., that which is placed in the callback to forever—to be run at specific intervals in the future.

The frame-based approach is in many way simpler; but it involves a different way of approaching coding challenges, requiring a different logic. Therein lies the primary challenge of this lesson.

(((Students have encountered one approach to this: set speed Infinity, but then effectively break up the program into smaller chunks using calls to await done defer(). In a sense, however, this is a cheat. The minute delays that result from the processing of the program in small chunks rather than as a single whole accumulate, and effectively yeild the same result as if we had delayed execution of blocks of code using timers.)))

We conclude this section with some additional specifics about working with timers set up with forever. Animation created with code in the callback to a timer set up with forever will, by default, execute at speed Infinity, i.e., using frame-based animation. Calls to speed outside of this callback have no effect on this behavior. (On a more technical note, this is because speed is alias for window.speed, not turtle.speed.) However, sprite-specific calls to speed, including even turtle.speed, will cause animation associated with that sprite to follow queue-based logic. Also, note that in programs not otherwise set to operate using frame-based animation, the call to forever itself is placed in the animation queue.

Notes to activities

Each of the activities can be coded using frame-based animation. Remind students to default to use of speed Infinty in their programs. Students will invariably encounter situations for which it makes sense to mix queue-based and frame-based animation logic, but each of the activities in this lesson can be solved without resorting to that. At this point, the goal is for students to gain familiarity and comfort coding frame-based logic.

Using forever to add timers is the easy part of working with frame-based animations. The real challenge is coming up with the code that goes in the callback. In AnalogClocks, as the instructions make clear, the key is to employ logic and tools that keep the time accurate. In RandomTurtles the greater challenge is to come up with an algorithm that not only keeps the sprites inside the visible screen (or some other designated area), but does so in a visually appealing way. Encourage students to come up with visually appealing solutions, where the motion looks good, rather than taking the easy way out, such as the equivalent of if not inside(window) then home().

It should be fairly clear in the clock activity that the second approach will yield the more accurate clock. The difference is especially noticable if the user navigates away from that tab in the browser, as the programs won't advance when the tab is not active. Students are often inclined to use sprites to represent clock hands, which they then rotate to reflect the current time. Frame-based animation facilitates an alternative approach: create a function that clears the screen and redraws the clock every time the timer fires.

Additional activities

  • Stopwatch: Code a program similar to a clock, but add buttons (i.e., a <label> element with a click-listener attached to it) to let the user stop and reset the stopwatch. Naturally, this program should make use of stop to remove the timers that run the clock. As with the lesson's AnalogClock activity, there are different ways to program this solution, some of which are more robust than others. This solution simply advances the timer hands based on the timing of the events firing. Like the preferred solution to the AnalogClock program, this model solution bases its measure of elapsed time on recurring calls to Date.now().
  • Add a SecretMessage to the screen by creating a label that has the same text color as the background. Use a timer to quickly add a potentinally unbounded number of small, randomly-placed colored dots to the screen. As the dots accumulate, the message will eventually be revealed. Keep track of how many dots are needed before you can spot the message!
  • GravityBounce: The Modeling Motion! lesson explored algorithms to simulate realistic motion. Apply the techniques discussed in that lesson in a frame-based-animation context, running your animation perpetually using timers set up with forever.
  • While not of great concern with today's light-emitting-diode (LED) computer monitors, a few decades ago, with cathode-ray-tube (CRT) monitors, screen burn-in was a real concern. Essentially, light that stayed on in the same spot of the screen too long could leave a permanant mark in the screen, that could be even be seen when the power was subsequently turned off. To prevent such damage, computers ran screen saver programs, which guaranteed that the pattern on the screen changed frequently enough. There were a variaety of screensaver options, some very elaborate. Create your own custom ScreenSaver script, which, of course, should run perpetually based on .
  • Code your own version of the classic Snake game. Use keydown events to capture user input for directing the snake, but use a timer to carry out the game's logic. Visually, your snake should be reprented by a continuous group of simple blocks.

    There are various options for representing this in your code. One approach is to use individual sprites for each segment, which you can keep track of using arrays. (An alternative algorithm, which draws boxes on the screen but keeps the length correct using a separate tail-end sprite, is used in this sketch.) Because the head of the snake is of special interest, you might choose to leave that out of the array. Alternatively, use conditional logic within the array. Rather than create and update an index explicitly, we can use this alternative version of the for loop:

    for segment, i in snake
      if i == 0
        head = segment
      else
        #more code...
  • KeepEmInsky: The following lesson, Event Polling!, introduces techniques to efficiently and effectively incorporate user-input driven events into your programs. Although it tends not to work as well, we can incorporate events into programs with timing events using the methods introduced in the preceeding fe lessons. The goal of this activity is to code a script that creates a game in which the user is tasked with keeping turtle sprites in the screen as long as possible. Begin this effort with a modified copy of your RandomTurtles script, the primary difference being that sprites should remove themsleves from the program when they reach the edge of the screen, rather than follow some algorithm to stay on screen. Add a click listener to each of the sprites—do this efficiently, using the jQuery each method. Add a timer to the screen to keep score.

Beyond the lesson

forever vs. setTimeout

As the lesson notes, forever (and the stop method used in conjunction with it) is a Pencil Code alternative to the built-in JavaScript method setInterval. The difference between forever and setInterval largely come down to three features.

First, forever is executed as part of the Pencil Code animation queue. If speed is set to Infinity at the beginning of the script, this has no effect (as the full program executes using frame-based animation). Otherwise, animations arising from code preceding the call to forever will complete before the timer that associated with that call to forever starts.

A second, more subtle difference, which has to do with the functioning of the associated stop method, will be discussed [NEED AN HREF]later in this document. The obvious difference, important to us here, is that they differ in their arguments. The syntax for forever is forever fps, callback, or forever callback when using the default refresh rate (fps) of 30. The syntax for the built-in alternative is setInterval callback, delay, where delay is specified in milliseconds.

The reason for pointing out these differences at this early stage of students' development is because setInterval's use of a delay argument more directly reflects the nature of timers. The better students understand this, the more likely they are to write effective code. Of particular concern is that when specifying the fps argument, one can get the impression that we are altering the speed at which the computer processes animations. But this is not the case. Animations are still carried out as fast as the computer can manage. However, we delay the processing of codethat includes the instructions for subsequent animations.

An important upshot from the preceding discussion is that there is no such thing as a "forever loop". True, the code specified in the callback is called on a recurring basis, but the coding structure isn't a loop in the sense that structures created with while or for are. forever is a function. It sets up a timer that schedules when to run a callback. The schedule it creates invokes the callback repeatedly, at fixed intervals.

mixing frame- and queue-based animations

The upshot of the last points of the "More about the lesson" discussion above is that we can easily come up with code that mixes frame-based and queue based animation.

It is possible, in fact often desireable, to mix frame- and queue-based animations.

In fact, the distinction is somewhat arbitrary... in the sense that ultimately, all animations in JavaScript are frame-based. It's just that in Pencil Code (and jQuery more broadly), tools are constructed which let us code using the logic of queue-based, without having to trouble ourselves with translating that to frame-based. Oftentimes, it can be easier to think through logic that way.

stop vs. clearInterval

window.clearInterval

setTimeout

A subtle difference is that Pencil Code tracks calls to forever, which facilitate having the stop methodkeeps track

What can go wrong

scoping problems

Variables referenced in callbacks, and not declared in the containing scope, exist only for the scope of that callback. This holds for callbacks passed to forever just like any other higher-order function.

break vs. stop

Because it is not a loop, the timing event established with forever cannot be interrupted with break, the statement used to terminate for and while blocks. Similar to how, with user-interface related events explored in recent lessons, we need to remove the listener with a subsequent function call; As the lesson notes, a timer set up with forever is removed use stop.

FIX/EDIT: Note that, unlike the effect that break has on loops, the current callback is not immediately interrupted.

Mixing frame- and queue-based animation

As these notes discuss, it is possible, and often desireable, to mix frame- and queue-based animation. However, for novices, this is often done inadvertently, which can lead to confusing results. The remedy at this point is straightfoward: stick solely to frame-based animation, by beginning each script with the statement speed Infinity.

Pedagogy

Tools mediate thinking

The seminal russion educational psychologist Lev Vygotsky believed that cultural tools (including real tools and symbolic tools) play important roles in cognitive development. Though his focus was on childhood development, the concept, broadly accepted in the learning sciences, is that the nature of the tools and experience you have shape how you understand the world. All higher-order mental processes, such as reasoning and problem solving, are mediated by psychological tools, including signs, symbols, and language. In the context of this lesson, this means that the coding concepts we know shape how we approach and solve coding challenges.

A seemingly minor difference between the built-in setInterval method and Pencil Code's forever is the arguments used to set the rate at which the associated callback is executed. Pencil Code's use of frames-per-second (fps) was presumably chosen because it is in line with rate, with a bigger number associated with a faster rate of animation. However, the very term "frames" is a misnomer, because the timing event listener set up with forever has nothing inherently to do with animations.

The emphasis in this lesson is on the underlying logic of timing events, and using them explicitly to control how animations are carried out, rather than our prior implicit use of them when working with queue-based animation. In the latter, timing events are still at play, it is just that jQuery and Pencil Code do all the necessary translations for us.

One reason to learn about setInterval has to do with transfer: one day, students will graduate from Pencil Code to a less scaffolded coding environment, at which point they will be foreced to work with the language's built-in mehtods.

But it is also important to have students learn about the setInterval function because it makes the link between timing events and our code more clear. No one speaks about "setInterval loops", but within the Penci, at least not in the sense of the logical workings behind a for or while loop.

First learn to walk, then run...

Timing events open up exciting new programming opportunities, in particular with respect to making programs interact seamlessly with user-input, such as captured through keyboard and mouse events. However, activities focusing on such features are noticably absent from this lesson. The reason for this is to get students focused on the basics of timing events, without risking getting overwhelmed by too much, too soon.

The activities in this lesson are intentially chosen to be rather straightforward applications of timing events and frame-based animation. Even still, there remains significant logical challenges working with this "new way of thinking". Adding events can quickly cause the complexity to the programs to ramp up, and ultimately detract from the main lesson.

An additional reason for holding back, for now, is that adding event listeners using the methods explored in prior lessons is typically a suboptimal way to allow user interaction. The next lesson, Event Polling!, introduces an approach that is at once more straightforward to code and produces a better result.

Technicalities

Ninja Content

Use of timer events can be nuanced. A great exploration is provided in the JavaScript Ninja book... (Race conditions??)

Reasons for delays longer than specified: https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#reasons_for_delays_longer_than_specified; lots of other nuances too...

tick

Earlier versions of jQuery-turtle.js included a function called tick that worked much the same as forever. Only one timer could be set up in a program using tick, whereas forever can be used to set up multiple timers. tick was superceded by forever and now is deprecated (i.e., maintained only for legacy code).

unbounded iteration

In prior sections of these notes, it was pointed out that forever doesn't created a loop. But because it repeats infinitely, it can be confused with an infinite loop. With iterative programming, such a loop can be created with while true.

We can have infinite loops with recursion too... When a function calls itself, with no stopping point.

As explained above, timers work on a different logic....

Optional first argument

Many Pencil Code functions differ from built-in functions in that the order of their arguments is often very flexible. For example, the pen method can accept several arguments. According to the Pencil Code documentation, pen blue, 10 is valid. However, pen blue, 10 will work just as well. Pencil Code provides this flexibility to make it more forgiving to novice coders and to anticipate and automatically eliminate some of the many bugs they might otherwise introduce into their programs.

Unlike pen, the order of the arguments to forever cannot be reversed, as illustrated in this simple script. However, what makes forever unusual is that the optional argument, fps, comes first. Presumably, the author of Pencil Code put fps first to facilitate using anonymous callbacks with forever, as illustrated in the coding block examples in the palette, e.g.,

If the callback argument came first, it would still be possible to define it anonymously, but novice coders would certainly struggle with the additional parentheses required to make the code valid.

As a potentially interesting aside, consider how Pencil Code accomplishes letting the first argument be optional. From jQuery-turtle.js, we have:

function forever(fps, fn) {
  if (!fn && 'function' == typeof(fps)) {
    fn = fps;
    fps = 30;
  }
  var ms = Math.max(Math.floor(1000/Math.max(1/(24*60*60), fps)), 0);
  // more code...
}

Pencil Code makes use of truthy-falsey logic !fn, which is true if the second argument, fn, is undefined. When that's the case, it reassigns the first argument to be the function reference and sets the default fps to 30.

(Notice how the forever function also then converts fps to ms, i.e., the delay input needed for a subsequent call to setInterval.)