How do you schedule timed events in Flask?

2019-07-15 04:05发布

问题:

Here's some code:

from flask import Flask, request
import time, threading

class MyServer(Flask):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.reset()

    def reset(self):
        self.string = "hello"

application = MyServer(__name__)

@application.route("/set")
def set():
    application.string = request.args["string"]
    threading.Timer(10, application.reset()).start()
    return request.args["string"] + " stored for 10 seconds"

@application.route("/get")
def get():
    return application.string

if __name__ == "__main__":
    application.debug = True
    application.run()

My expectation/goal is that if you visit the endpoint /set?string=foo then for the next 10 seconds the app will return "foo" any time you visit /get, and from then on it will return "hello" until you hit the /set endpoint again.

Instead, if I hit /set?string=foo and then immediately '/get', the app returns "hello" and I see "TypeError: 'NoneType' object is not callable" in the console. Can anyone help?

回答1:

The following is incorrect:

threading.Timer(10, application.reset()).start()

The second argument to Timer needs to be a function, but you're actually calling the reset() function and thus passing the result of that method call to Timer. It's as if you've done the following...

result = application.reset()
threading.Timer(10, result).start()

What you probably want to do instead is the following...

threading.Timer(10, application.reset).start()

That being said, I would be very hesitant to use this solution for anything more than a toy project: depending on how your Flask application is deployed, you might actually have multiple Flask processes running at the same time. In that case, this solution will only change the process you are currently accessing. Additionally, spawning a new thread in each request could cause quite a bit of overhead depending on your load.

A better approach might be to save this data into the user's session (a cookie), or into a database. You could use a system like Celery or another message queue to run asynchronous code.

If you need data to "expire" after a certain amount of time, also consider setting the date that the data should expire, and then introducing code which checks for the expiration. Thus, your "set" method could synchronously set an expiration time, and the "get" endpoint could check if the expiration time has elapsed and choose which value to return based on that. Now you don't need an asynchronous call to be made.