Python
Timer
Scheduling
Automation
Programming

Execute specified function every X seconds

Master System Design with Codemia

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

Introduction

Running a function at regular intervals is a common requirement for monitoring, polling, cleanup tasks, and heartbeat mechanisms. In Python, the main approaches are time.sleep() in a loop (simplest), threading.Timer (non-blocking), sched module (event scheduler), and third-party libraries like schedule and APScheduler (feature-rich). In JavaScript, use setInterval(). Each approach has different tradeoffs in precision, blocking behavior, and error handling. This article covers the idiomatic solution for each language and use case.

Python: time.sleep() Loop

The simplest approach — runs the function in the current thread:

python
1import time
2
3def check_status():
4    print(f"[{time.strftime('%H:%M:%S')}] Checking status...")
5
6# Run every 5 seconds (blocks the thread)
7while True:
8    check_status()
9    time.sleep(5)

This blocks the thread, so nothing else can run. The actual interval is execution_time + 5 seconds, not exactly 5 seconds.

Drift-Corrected Loop

python
1import time
2
3def run_every(interval, func):
4    next_run = time.monotonic()
5    while True:
6        func()
7        next_run += interval
8        sleep_time = next_run - time.monotonic()
9        if sleep_time > 0:
10            time.sleep(sleep_time)
11
12run_every(5, check_status)
13# Compensates for execution time to maintain consistent intervals

Python: threading.Timer (Non-Blocking)

python
1import threading
2import time
3
4def repeated_task():
5    print(f"[{time.strftime('%H:%M:%S')}] Running task")
6
7class RepeatingTimer:
8    def __init__(self, interval, function):
9        self.interval = interval
10        self.function = function
11        self._timer = None
12        self._running = False
13
14    def _run(self):
15        self._running = False
16        self.start()  # Reschedule
17        self.function()
18
19    def start(self):
20        if not self._running:
21            self._timer = threading.Timer(self.interval, self._run)
22            self._timer.daemon = True
23            self._timer.start()
24            self._running = True
25
26    def stop(self):
27        if self._timer:
28            self._timer.cancel()
29        self._running = False
30
31# Usage
32timer = RepeatingTimer(5, repeated_task)
33timer.start()
34
35# Do other work while timer runs in background
36time.sleep(20)
37timer.stop()

Python: schedule Library

The schedule library provides a human-readable API:

python
1import schedule
2import time
3
4def job():
5    print("Running scheduled job...")
6
7def send_report():
8    print("Sending daily report...")
9
10# Schedule jobs
11schedule.every(10).seconds.do(job)
12schedule.every(5).minutes.do(job)
13schedule.every().hour.do(job)
14schedule.every().day.at("10:30").do(send_report)
15schedule.every().monday.do(job)
16
17# Run the scheduler
18while True:
19    schedule.run_pending()
20    time.sleep(1)

Install with pip install schedule.

With Arguments

python
1import schedule
2
3def greet(name):
4    print(f"Hello, {name}!")
5
6schedule.every(3).seconds.do(greet, "Alice")
7
8# Or with a lambda
9schedule.every(3).seconds.do(lambda: greet("Bob"))

Python: asyncio (Async)

python
1import asyncio
2
3async def periodic_task(interval, func):
4    while True:
5        func()
6        await asyncio.sleep(interval)
7
8async def check_api():
9    print("Checking API...")
10
11async def main():
12    # Run multiple periodic tasks concurrently
13    task1 = asyncio.create_task(periodic_task(5, lambda: print("Task 1")))
14    task2 = asyncio.create_task(periodic_task(10, lambda: print("Task 2")))
15
16    await asyncio.gather(task1, task2)
17
18asyncio.run(main())

JavaScript: setInterval()

javascript
1// Run every 3 seconds
2const intervalId = setInterval(() => {
3  console.log(`[${new Date().toLocaleTimeString()}] Running task`);
4}, 3000);
5
6// Stop after 15 seconds
7setTimeout(() => {
8  clearInterval(intervalId);
9  console.log("Stopped");
10}, 15000);

With Async/Await

javascript
1async function fetchData() {
2  const response = await fetch("https://api.example.com/status");
3  const data = await response.json();
4  console.log("Status:", data.status);
5}
6
7// setInterval does not await async functions
8// Use recursive setTimeout instead
9async function poll(fn, interval) {
10  await fn();
11  setTimeout(() => poll(fn, interval), interval);
12}
13
14poll(fetchData, 5000);

Python: APScheduler (Production-Ready)

python
1from apscheduler.schedulers.background import BackgroundScheduler
2from apscheduler.triggers.interval import IntervalTrigger
3import time
4
5def my_job():
6    print(f"[{time.strftime('%H:%M:%S')}] Job executed")
7
8scheduler = BackgroundScheduler()
9scheduler.add_job(my_job, IntervalTrigger(seconds=10))
10scheduler.add_job(my_job, 'cron', hour=2, minute=30)  # At 2:30 AM
11scheduler.start()
12
13try:
14    while True:
15        time.sleep(1)
16except KeyboardInterrupt:
17    scheduler.shutdown()

Install with pip install apscheduler.

Comparison

ApproachLanguageBlocking?Error HandlingPrecision
time.sleep loopPythonYesManual try/exceptDrifts with execution time
threading.TimerPythonNoManualModerate
schedulePythonYes (event loop)ManualSecond-level
asyncio.sleepPythonNo (async)try/except in coroutineModerate
APSchedulerPythonNo (background)Built-in retry/loggingHigh
setIntervalJavaScriptNoUncaught → consoleMillisecond

Common Pitfalls

  • Not handling exceptions in the scheduled function: If the function throws an exception in a time.sleep loop, the entire loop stops. Wrap the function call in try/except to ensure the loop continues: try: func() except Exception as e: log(e).
  • Accumulating drift with time.sleep(interval): The actual interval becomes execution_time + sleep_time. For precise intervals, compute the next run time and sleep only the remaining duration using time.monotonic().
  • Using setInterval for async operations in JavaScript: setInterval does not wait for async functions to complete. If the function takes longer than the interval, multiple executions overlap. Use recursive setTimeout with await instead.
  • Forgetting to make timer threads daemon threads: Non-daemon timer threads prevent the Python process from exiting. Set timer.daemon = True or call timer.cancel() during shutdown to allow clean process termination.
  • Using schedule library without calling run_pending() in a loop: schedule.every(10).seconds.do(job) only registers the job — it does not execute it. You must call schedule.run_pending() repeatedly in a loop for jobs to actually run.

Summary

  • Use time.sleep() in a loop for simple, blocking periodic tasks
  • Use threading.Timer or the schedule library for non-blocking background execution
  • Use asyncio.sleep() for async applications with multiple concurrent periodic tasks
  • Use APScheduler for production systems that need cron scheduling, persistence, and error handling
  • In JavaScript, prefer recursive setTimeout over setInterval for async operations to prevent overlapping executions

Course illustration
Course illustration

All Rights Reserved.