CodePark/ questions / Async code, timers & time
START PRACTICINGPRACTICE
Backend question · Backend testing › Async code, timers & time

This test uses Jest's modern fake timers. What happens when it runs?

jest.useFakeTimers();
async function withRetry(fn) {
  try { return await fn(); } catch {
    await new Promise((r) => setTimeout(r, 1000));
    return fn();
  }
}
test('retries once', async () => {
  const fn = jest.fn().mockRejectedValueOnce(new Error('down')).mockResolvedValueOnce('ok');
  const p = withRetry(fn);
  jest.advanceTimersByTime(1000);
  await expect(p).resolves.toBe('ok');
});
It fails with a test timeout: the retry timer is only created after the rejection is handled in a microtask, after the clock was already advanced; await jest.advanceTimersByTimeAsync(1000) fixes it

The test fails with a timeout. advanceTimersByTime is synchronous, and it only fires timers that already exist. The retry's setTimeout is created only after await fn() settles. So the order is:
• The clock moves forward first.

• Nothing fires, because the timer does not exist yet.

•
p never settles, and Jest's own timeout ends the test.

The async variant,
advanceTimersByTimeAsync, lets pending promise callbacks run before it moves the clock. So the timer exists in time and fires.

"It passes, because
advanceTimersByTime fires the 1-second timer straight away" is the tempting wrong answer. It would be true only if the timer already existed when the clock moved.