4 Creative Ways to JavaScript Timing in Browsers
Published: 2020-02-15 13:38:28

---
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/xgjix93m2yo2vkrd2n3j.jpg
title: 4 Creative Ways to JavaScript Timing in Browsers
subtitle: Interesting alternatives for setTimeout, setInterval, and RAF
type: tech
published: true
---
<cite><small>Photo by Lukas Blazek on Unsplash</small></cite>

This article assumes the availability for Web APIs, therefore, most methods suggested here don't work in NodeJS.

## Using an infinite synchronous loop in a Web Worker (not Service Worker)

Since Web Workers are essentially web threads, you can infinitely loop inside them without blocking the main thread. This gives you access to sub-millisecond time resolutions. This is especially good to make time critical decisions within the worker, and let the main thread know when (_accurately_) you see fit. Eg. Rerender something whenever microseconds are a prime number. To get access to microseconds, you can use [`performance.now`](https://developer.mozilla.org/en-US/docs/Web/API/Performance/now).

#### Pros and cons

##### Pros

1.  Sub-millisecond resolution.
2.  Near zero cost on the UI thread.
3.  Fully asynchronous from a UI thread perspective. Thanks to Web Workers messaging design.
4.  Safe ending. Unlike `setInterval`, calling `worker.terminate` guarantees no more messages will be received. Quoting MDN, "The terminate() method of the Worker interface immediately terminates the Worker. This does not offer the worker an opportunity to finish its operations; it is simply stopped at once."

##### Cons

1.  Even though you can make sub-millisecond decisions, messaging back to the UI thread is asynchronous. You can't render as timely as you make the decision in the worker.
2.  Keeps a thread fully occupied. Might be heavy on phone batteries.
3.  Requires Web Worker support.
4.  Doesn't pause when the tab isn't focused.

#### Codesandbox example

{% codesandbox web-worker-timer-eiczf %}

## Using CSS animations for time events (particularly `animationiteration`)

If you create a `div` (don't use a div though, see point 2 of cons) with an infinite animation. You can subscribe to its `animationiteration` event and get notified whenever `animation-duration` passes.

##### Pros

1.  Automatic suspension when the tab isn't in focus. The event doesn't fire at all when the tab isn't in focus. You don't need to worry about jammed calls that will all run at once when the tab is in focus again.
2.  Automatic clean up when the your hidden div is removed from the DOM. For example, if you have a React component that renders the time, you don't need to do anything on unmounting. The div will me removed and the event will not fire anymore.
3.  This is subjective but the subscription logic is beautiful. Eg. `.addEventListener("animationiteration", fun)`.
4.  Super clean way to delay the start of the timer by using `animation-delay`.

##### Cons

1.  A little too clever, might confuse your teammates/contributors.
2.  Depends on the DOM and CSSOM. Other CSS rules can interfere with yours. That's why I suggest to create an arbitrary non-existent tag like `<just-a-timer-element></<just-a-timer-element>`. Maybe create a custom element with the CSS animation code neatly tucked within? 🤯 (don't come after me, kthx).
3.  Doesn't work if the element has `display: none;`.
4.  Inaccurate. According to my testing, it can be off by a full `1ms`. You can try yourself in the example CSB below.

#### Codesandbox example

{% codesandbox css-animation-timer-fvssk %}

### Using SVG <animate> tag (SMIL Animations)

If you do `animate.addEventListener('repeat', fun)`, your function will be called every `dur` (1s) period.

##### Pros

1.  Works even when the SVG is `display: none;`.
2.  Stops automatically when the SVG is removed from the DOM.
3.  Doesn't begin until full page load.
4.  Auto-pauses when the tab isn't in focus.

##### Cons

1.  A little too clever, might confuse your teammates/contributors.
2.  Depends on the DOM and CSSOM. Same caveats as above. Other CSS rules can interfere with your configuration.
3.  Isn't supported in IE and Edge (before Chromium).
4.  Inaccurate. According to my testing, it can be off by a whopping 15ms. You can try yourself in the example CSB below.
5.  Doesn't begin until full page load. Yup, can be a bug and a feature.

#### Codesandbox example

{% codesandbox css-animation-timer-u8zun %}

## Using Web Animations API

[Web Animations API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API) allows you to animate DOM elements from within JavaScript.

Interestingly, you can animate unmounted elements! This gives you access to a timing mechanism in pure JS (and Web APIs).

Here is an alternative implementation of `setTimeout`

```js
function ownSetTimeout(callback, duration) {
  const div = document.createElement('div');

  const keyframes = new KeyframeEffect(div, [], { duration, iterations: 1 });

  const animation = new Animation(
    keyframeRequestAnimationFrames,
    document.timeline
  );

  animation.play();

  animation.addEventListener('finish', () => {
    callback();
  });
}
```

Neat, isn't it?

##### Pros

1.  Atomic. Doesn't need DOM interactions.
2.  Easy to understand by an unfamiliar person.
3.  Pauses when the tab isn't focused.

##### Cons
1.  Still a proposal. Don't use in production.
1.  Dire support situation. Probably only works in Chromium.
2.  Still a little counterintuitive.
3.  Pauses when the tab isn't focused. Can be bad if used as an alternative for `setTimeout`.
4.  Can't be used for intervals. Only `onfinish` event is available.
5.  Inaccurate. According to my testing, ±5ms can happen easily.

#### Codesandbox example

{% codesandbox svg-animate-as-timer-duv5q %}


## This was fun

I realize this is a niche article. But I felt compelled to write it because I always thought `setTimeout` and `setInterval` were the only paths towards asynchronous delays and intervals. But turns out there are a few other options! And who knows, someone might have some strange constraints and might find this useful.