programming
code execution
time intervals
coding techniques
software development

Run certain code every n seconds

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Running code every n seconds sounds simple, but the right implementation depends on what "every n seconds" actually means. Do you want fixed-delay repetition, fixed-rate scheduling, UI-safe callbacks, or background work that survives restarts? Those are different problems.

A good timer loop also needs cancellation and error handling. Without those, a periodic task may drift, overlap, or keep running after the rest of the application is trying to shut down.

Understand Fixed Delay Versus Fixed Rate

There are two common timing behaviors.

Fixed delay means the program waits n seconds after one run finishes before starting the next run. The runtime of the task is part of the interval.

Fixed rate means the task aims for a clock-based schedule such as once every 5 seconds regardless of how long the last run took. If the task runs too slowly, executions may bunch up or fall behind.

For many applications, fixed delay is the safer default because it avoids overlapping executions.

A Simple Python Loop

For small scripts, a while True loop with time.sleep is often enough:

python
1import time
2
3
4def do_work():
5    print("running")
6
7
8while True:
9    do_work()
10    time.sleep(5)

This is simple and readable. It behaves like a fixed-delay loop because the sleep happens after the work.

The downside is that this design is single-threaded and blocks the current flow. That is fine in a script but not ideal inside servers or GUI programs.

Add Stop Control and Error Handling

A better pattern wraps the loop so it can stop cleanly:

python
1import threading
2import time
3
4
5stop_event = threading.Event()
6
7
8def run_every(interval_seconds: float):
9    while not stop_event.is_set():
10        try:
11            print("periodic work")
12        except Exception as exc:
13            print(f"task failed: {exc}")
14
15        if stop_event.wait(interval_seconds):
16            break
17
18
19thread = threading.Thread(target=run_every, args=(5,), daemon=True)
20thread.start()
21
22time.sleep(12)
23stop_event.set()
24thread.join()

This keeps the loop responsive to shutdown and makes failures visible instead of silently killing the scheduler thread.

Use Async Scheduling in Async Programs

If the rest of the application already uses asyncio, keep the timer async too:

python
1import asyncio
2
3
4async def run_every(interval_seconds: float):
5    while True:
6        print("async periodic work")
7        await asyncio.sleep(interval_seconds)
8
9
10asyncio.run(run_every(5))

This avoids blocking the event loop and plays nicely with other asynchronous tasks.

For more precise control, you can measure time explicitly and schedule against the clock rather than sleeping blindly after each iteration.

Browser and UI Example With JavaScript

In browser or Node.js code, setInterval is the obvious starting point:

javascript
1function runTask() {
2  console.log("running every 5 seconds");
3}
4
5const timerId = setInterval(runTask, 5000);
6
7setTimeout(() => clearInterval(timerId), 16000);

This is convenient, but it can become problematic if the task sometimes takes longer than the interval. In that case, a recursive setTimeout often behaves more predictably because it naturally creates fixed-delay scheduling.

Avoid Overlapping Work

One of the biggest design mistakes is allowing a periodic task to start again before the previous run finished. A simple guard prevents accidental overlap:

python
1import threading
2import time
3
4
5running = False
6
7
8def run_task():
9    global running
10    if running:
11        return
12
13    running = True
14    try:
15        print("start")
16        time.sleep(3)
17        print("end")
18    finally:
19        running = False

If your interval is shorter than the task duration, you need to decide whether to skip runs, queue them, or run them concurrently. That is a product decision, not only a coding detail.

Common Pitfalls

The biggest mistake is assuming "every n seconds" has one universal implementation. The right answer depends on whether the environment is synchronous, asynchronous, UI-bound, or server-side.

Another common issue is ignoring cancellation. A timer without a stop path tends to become a shutdown bug later.

People also forget about task duration. If the work takes longer than the interval, naïve scheduling can overlap executions and create subtle state corruption.

Finally, do not use a sleep loop where a real scheduler or job system is required. Long-lived production scheduling often deserves infrastructure beyond an in-process timer.

Summary

  • Decide first whether you need fixed-delay or fixed-rate behavior.
  • A simple sleep loop is fine for small scripts but not for every environment.
  • Add cancellation and error handling to any real periodic task.
  • Keep the scheduling model aligned with the runtime, such as asyncio for async Python or setInterval in JavaScript.
  • Prevent overlapping runs unless concurrency is intentionally part of the design.

Course illustration
Course illustration

All Rights Reserved.