Timers

Notes

The Event Loop

Javascript follows a simple concurrency model, based on an “event loop”:

Timers

There are a number of Javascript functions that allow us to add custom “events” on the event loop, to be executed at an appropriate time. Most of these take the form:

timerFunction(f, milliseconds);

Where we “register” the function f to be executed after a given number of milliseconds. This is a “desired” time: If something else is executing when that time comes, then the function will be queued to run at the next available opportunity.

You should not really expect the function f to receive any arguments, nor to have a good this object.

Here are the main methods:

setTimeout
Registers a function to run once at some time in the future.
setInterval
Registers a function to run repeatedly every so often.
clearTimeout / clearInterval
These can be used to clear a previously set timer. They use the return values of the corresponding “set-” methods.

Here is a simple example:

var f = (function() {
  let i = 0;
  return function() {
    i += 1;
    console.log(i);
  }
}());
var id = setInterval(f, 1000);
// Watch the pretty numbers
// Run next line when you want it to stop
clearInterval(id);

Function Idioms

We look here at some function idioms that timers allow.

Delay

This is a simple idiom. It is given a function f and a delay time in milliseconds, and returns a function that when called sets up a delayed call to f, passing along whatever arguments it is given.

This idiom can be handy if we want to pass a function to someone else to execute, but we want to make sure they can’t run it right away.

function delay(f, milli) {
  return function delayed() {
    setTimeout(() => f.apply(this, arguments), milli);
    return;
  };
}

// Sample run:
var g = delay(console.log, 5000);
g.call(console, "5 seconds later");
g("5 seconds later");

Throttle

Given a function and an interval, it will only allow the function to be called once in that interval. Further attempts to call the function during that interval will simply be ignored. Useful for events that are coming too fast for us to process them all.

function throttle(f, interval) {
  let available = true;
  // function "wait" used in setTimeout
  function wait() { available = true; };
  return function throttled() {
    if (available) {
      available = false;
      setTimeout(wait, interval);
      f.apply(this, arguments);
    }
  };
}

// Sample run:
var g = throttle(console.log, 5000);
g.call(console, "Prints right away"); g.call(console, "gets lost");

Debounce

Given a function and an interval, it will only run the function after that amount of time has passed since the last time the function was invoked, and it will call it in the exact same way as the last attempted call. So if you call the function it will start waiting for that amount of time, and if it is called again it will reset the timer and start waiting again.

Essentially this function will not run as long as it is getting called.

Useful when we have incoming input, and we only want the function to execute once that input is completed (say once someone has stopped moving their mouse or stopped typing).

function debounce(f, wait) {
  let timer = null;

  return function debounced() {
    if (timer != null) { clearTimeout(timer); }
    timer = setTimeout(() => f.call(this, arguments), wait);
  };
}

// Sample run
document.onmousemove = function() { console.log("All the time!"); };
document.onmousemove = debounce(function(ev) { console.log("only when I stop!", ev); }, 3000);