Skip to content

Instantly share code, notes, and snippets.

@oculus42
Last active November 22, 2023 22:14
Show Gist options
  • Save oculus42/68f7058ec5a852ab4ea2b05839faf302 to your computer and use it in GitHub Desktop.
Save oculus42/68f7058ec5a852ab4ea2b05839faf302 to your computer and use it in GitHub Desktop.
Starting intervals at the edge of the second can help expose timer/interval defects.

JavaScript Timeouts and Intervals can experience hard to reproduce defects. This script tries to help.

Browser Variation

Chrome attempts to provide the timer with the "closest to original interval" without going under, so the interval is inconsistent. This causes jitter.

const ChromeTimings = [
  { second: 17, timestamp: 37998 },
  { second: 18, timestamp: 38998 },
  { second: 20, timestamp: 40000 }, // No 19
  { second: 21, timestamp: 41001 },
  { second: 22, timestamp: 42000 },
  { second: 22, timestamp: 42998 }, // Two 22
  { second: 23, timestamp: 43999 },
  { second: 25, timestamp: 45001 }, // No 24
  { second: 25, timestamp: 45998 },
  { second: 27, timestamp: 47000 }, // No 26
];

Firefox and Safari guarantee a minimum of the specified interval, but this causes drift from the original time.

const FirefoxTimings = [
  { second: 17, timestamp: 37999 },
  { second: 18, timestamp: 38999 },
  { second: 20, timestamp: 40003 }, // No 19
  { second: 21, timestamp: 41009 },
  { second: 22, timestamp: 42013 },
  { second: 23, timestamp: 43027 },
  { second: 24, timestamp: 44026 },
  { second: 25, timestamp: 45030 },
  { second: 26, timestamp: 46036 },
];

const SafariTimings = [
  { second: 27, timestamp: 87998 },
  { second: 28, timestamp: 88998 },
  { second: 29, timestamp: 89999 },
  { second: 30, timestamp: 90999 },
  { second: 31, timestamp: 91999 },
  { second: 33, timestamp: 93000 }, // No 32
  { second: 34, timestamp: 94000 },
  { second: 35, timestamp: 95000 },
  { second: 36, timestamp: 96000 },
  { second: 37, timestamp: 97000 },
]
/**
* Try to execute a function just before the second ticks over.
* @param {function} fn - the function to run
*/
export const runJustBeforeSecond = (fn) => {
// How far off from the exact second are we?
const offset = 1000 - (Date.now() % 1000);
// We're trying for the last few milliseconds of a
// given second, so an interval will cross the boundary
if (offset > 995) {
// Run if we're already there
fn();
} else {
// Shaving off 8 ms usually works, but it's inexact.
// It only improves the odds, not guarantee.
setTimeout(fn, offset - 8);
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment