63

I want to know how to call a function after a certain time. I have tried time.sleep() but this halts the whole script. I want the script to carry on, but after ???secs call a function and run the other script at the same time

drnessie
  • 877
  • 2
  • 12
  • 25

5 Answers5

110

Have a look at threading.Timer. It runs your function in a new thread.

from threading import Timer

def hello():
    print "hello, world"

t = Timer(30.0, hello)
t.start() # after 30 seconds, "hello, world" will be printed
Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
  • multithreading... hmm, sounds familiar. Isn't that were there is more than one process? Well anyway that looks right. if I use t.start() more than once, does it carry on, give me an error or restart the timer? – drnessie Aug 08 '10 at 08:21
  • @drnessie: "Isn't that were there is more than one process?" No - it's when you have more than one thread. "if I use t.start() more than once, does it carry on, give me an error or restart the timer?" - You get an error 'RuntimeError: thread already started' which you can easily verify for yourself by duplicating the last line in the above source code and running it. – Mark Byers Aug 08 '10 at 08:33
  • Is there any way of getting rid of the error, apart from stopping then re-starting the timer? – drnessie Aug 09 '10 at 08:17
  • @drnessie: You could for example create a new timer. You probably don't want too many at once though, because each timer has its own thread. – Mark Byers Aug 09 '10 at 08:20
  • 3
    one last thing... does the thread destroy itself when the target function is finished? – drnessie Aug 10 '10 at 12:47
  • @MarkByers The Timer runs immediately after I call my function and does not wait for 30 seconds. Do you know why this would happen? – pandasCat Jul 12 '18 at 21:14
  • 3
    Adding this here in hopes that it answers follow up problems. Even though I used a timer, the function would immediately run without the timer. The reason was bc of this: https://stackoverflow.com/questions/16742043/python-threading-timer-start-immediately-not-at-specified-time – pandasCat Jul 13 '18 at 18:32
  • 2
    If you want to give arguments to your function you can do something like this: `t = Timer(30.0, hello, [arg1, arg2]) t.start()` – dpatryas Mar 14 '22 at 20:22
  • @dpatryas thank you this has driven me mad. Seems to run straight away if i provide args the standard way e.g ```t = Timer(5.0, hello("test"))``` but works correctly when i pass args as you suggested. – Grimeire Apr 14 '22 at 20:33
5

If you want a function to be called after a while and not to stop your script you are inherently dealing with threaded code. If you want to set a function to be called and not to worry about it, you have to either explicitly use multi-threading - like em Mark Byers's answr, or use a coding framework that has a main loop which takes care of function dispatching for you - like twisted, qt, gtk, pyglet, and so many others. Any of these would require you to rewrite your code so that it works from that framework's main loop.

It is either that, or writing some main loop from event checking yourself on your code - All in all, if the only thing you want is single function calls, threading.Timer is the way to do it. If you want to use these timed calls to actually loop the program as is usually done with javascript's setTimeout, you are better of selecting one of the coding frameworks I listed above and refactoring your code to take advantage of it.

jsbueno
  • 99,910
  • 10
  • 151
  • 209
  • 2
    This code is not working threading.Timer( 30.0, self.timestart(window) ).start() It is calling self.timestart, giving the arg window, but it is doing it Immediately, not after 30secs Would've asked Mark, but I've bothered him enough, and you know what your talking 'bout too :) – drnessie Aug 10 '10 at 15:18
2

If the non-block feature is not needed, just use time.sleep(5) which will work anywhere and save your life.

Pablo LION
  • 1,294
  • 6
  • 20
Barlas Apaydin
  • 7,233
  • 11
  • 55
  • 86
0

Okey, not a perfect solution but i used the following since my job was simple enough:

counter_time = 0
while True:
   time.sleep(0.1)
   counter_time = counter_time + 0.1

Now you have time just use if to do something at spesific time. My code was running inside the while loop so this worked for me. You can do the same or use threading to run this infinite loop together with your own code.

0

The most simplest way to do this is using a function I made, setTimeout:

import asyncio

#The function to execute
import asyncio

async def setTimeout(fn, time):
    await asyncio.sleep(time)
    fn()

def function():
    print("hi")

asyncio.run(setTimeout(function, 1))

I am currently trying to work on setInterval, like repeated timeouts, but I got an error.

NEW POST:

I figured out a mimic for setTimeout and setInterval from JavaScript, but in Python.

With this class you can:

  1. Set a timeout with an interval ID (through variable, not required).
  2. Set an interval in the same wise as a timeout.
  3. Set an interval with an ID, and then set a timeout that will cancel the interval in a given amount of time.
  4. Set a timeout with an ID and cancel the timeout with another setTimeout.

I know it seems complex but I needed all the functionality it had just like in JavaScript. Note that you will need to pre-create a function and use the function name as a parameter (see code).

#If I'm not mistaken, I believe the only modules you need are time and threading. The rest were for something I'm working on.
import time
import math
import pygame
import threading
import ctypes
from sys import exit

#Functions
class Timer:
    def __init__(self):
        self.timers = {}
        self.timer_id = 0
    
    def setInterval(self, fn, time, *args):
        def interval_callback():
            fn(*args)
            if timer_id in self.timers:
                self.timers[timer_id] = threading.Timer(time/1000, interval_callback)
                self.timers[timer_id].start()

        timer_id = self.timer_id
        self.timer_id += 1
        self.timers[timer_id] = threading.Timer(time/1000, interval_callback)
        self.timers[timer_id].start()
        return timer_id

    def clearInterval(self, timer_id):
        if timer_id in self.timers:
            self.timers[timer_id].cancel()
            del self.timers[timer_id]

    def setTimeout(self, fn, delay, *args, **kwargs):
        def timer_callback():
            self.timers.pop(timer_id, None)
            fn(*args, **kwargs)
        
        timer_id = self.timer_id
        self.timer_id += 1
        t = threading.Timer(delay / 1000, timer_callback)
        self.timers[timer_id] = t
        t.start()
        return timer_id
    
    def clearTimeout(self, timer_id):
        t = self.timers.pop(timer_id, None)
        if t is not None:
            t.cancel()
t = Timer()

#Usage Example
lall = t.setInterval(print, 1000, "hi")
t.setTimeout(t.clearInterval, 3000, lall)

Hope this helps.