DropsTheTicksBro
HTML
<main>
<h1>setInterval tick test</h1>
<p class="intro">
Start the timer, block the event loop, and see whether missed interval
ticks fire in a burst.
</p>
<section>
<h2>Live counts</h2>
<div class="stats">
<div><small>Ticks fired</small><b id="ticks">0</b></div>
<div><small>Seconds elapsed</small><b id="elapsed">0</b></div>
<div><small>Ticks lost</small><b id="lost">0</b></div>
<div><small>Condition fired</small><b id="fired">0</b></div>
</div>
</section>
<section>
<h2>Last 30 seconds</h2>
<canvas id="strip"></canvas>
<div class="key"><span>−30s</span><span>gap, not cluster</span><span>now</span></div>
</section>
<section>
<h2>Controls</h2>
<div class="buttons">
<button id="start">Start timer</button>
<button id="block" disabled>Block loop 10s</button>
...
CSS
*{box-sizing:border-box}
body{
margin:0;
padding:30px 18px;
background:#101219;
color:#e4e2db;
font:14px/1.5 ui-monospace,monospace
}
main{max-width:760px;margin:auto}
h1{font-size:16px;text-transform:uppercase;letter-spacing:.12em}
h2{
margin:0 0 14px;
color:#767c92;
font-size:11px;
font-weight:400;
text-transform:uppercase;
letter-spacing:.15em
}
.intro,.note,.key,td:last-child{color:#767c92}
.intro{max-width:62ch;margin-bottom:28px}
section{
margin-bottom:18px;
padding:20px;
background:#171a24;
border:1px solid #262b39
}
.stats,.buttons,.key{display:flex;flex-wrap:wrap}
.stats{gap:28px}
.stats div{min-width:120px}
small{display:block;color:#767c92;text-transform:uppercase}
b{display:block;font-size:32px;font-weight:400}
.stats div:nth-child(1) b{color:#6aa6ff}
.stats div:nth-child(2) b{color:#f0a830}
.stats div:nth-child(3) b{color:#ff6b5e}
canvas{
display:block;
width:100%;
height:68px;
background:#0c0e14;
border:1px solid #262b39
}
.key{justify-content:space-between;margin-top:7px;font-size:11px}
...
JavaScript
const $ = id => document.getElementById(id);
const ui = {
ticks:$("ticks"), elapsed:$("elapsed"), lost:$("lost"), fired:$("fired"),
strip:$("strip"), log:$("log"), start:$("start"),
block:$("block"), alert:$("alert"), reset:$("reset")
};
let started = 0, timer = 0, ticks = 0, fired = 0;
let lastTick = 0, history = [];
function elapsed() {
return started ? Math.floor((Date.now() - started) / 1000) : 0;
}
function tick() {
const now = Date.now();
ticks++;
history.push({at:now, gap:lastTick ? now-lastTick : 0});
lastTick = now;
if (now-started < 60000) fired++;
if (history.length > 400) history.shift();
render();
}
function render() {
const seconds = elapsed();
ui.ticks.textContent = ticks;
ui.elapsed.textContent = seconds;
ui.lost.textContent = Math.max(0, seconds-ticks);
ui.fired.textContent = fired;
ui.log.innerHTML = history.length
? history.slice(-10).reverse().map((item,i) => {
const time = new Date(item.at).toLocaleTimeString([], {
hour12:false,
hour:"2-digit",
minute:"2-digit",
second:"2-digit",
fractionalSecondDigits:3
...