Skip to content

Instantly share code, notes, and snippets.

@allanfreitas
Created May 27, 2019 01:16
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save allanfreitas/e2cd0ff49bbf7ddf1d85a3962d577dbf to your computer and use it in GitHub Desktop.
Save allanfreitas/e2cd0ff49bbf7ddf1d85a3962d577dbf to your computer and use it in GitHub Desktop.
the-best-way-to-repeatedly-execute-a-function-every-x-seconds-in-python
import time, traceback
def every(delay, task):
next_time = time.time() + delay
while True:
time.sleep(max(0, next_time - time.time()))
try:
task()
except Exception:
traceback.print_exc()
# in production code you might want to have this instead of course:
# logger.exception("Problem while executing repetitive task.")
# skip tasks if we are behind schedule:
next_time += (time.time() - next_time) // delay * delay + delay
def foo():
print("foo", time.time())
every(5, foo)
#
@allanfreitas
Copy link
Author

##Original Text
https://stackoverflow.com/a/49801719

If you want to do this without blocking your remaining code, you can use this to let it run in its own thread:

import threading
threading.Thread(target=lambda: every(5, foo)).start()

  • Exception handling: As far as possible on this level, exceptions are handled properly, i. e. get logged for debugging purposes without aborting our program.
  • No chaining: The common chain-like implementation (for scheduling the next event) you find in many answers is brittle in the aspect that if anything goes wrong within the scheduling mechanism (threading.Timer or whatever), this will terminate the chain. No further executions will happen then, even if the reason of the problem is already fixed. A simple loop and waiting with a simple sleep() is much more robust in comparison.
  • No drift: My solution keeps an exact track of the times it is supposed to run at. There is no drift depending on the execution time (as in many other solutions).
  • Skipping: My solution will skip tasks if one execution took too much time (e. g. do X every five seconds, but X took 6 seconds). This is the standard cron behavior (and for a good reason). Many other solutions then simply execute the task several times in a row without any delay. For most cases (e. g. cleanup tasks) this is not wished. If it is wished, simply use next_time += delay instead.

@jeppius
Copy link

jeppius commented Mar 27, 2021

how would you stop the periodic task in your code?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment