Is there a way in Flask to send the response to the client and then continue doing some processing? I have a few book-keeping tasks which are to be done, but I don't want to keep the client waiting.
Note that these are actually really fast things I wish to do, thus creating a new thread, or using a queue, isn't really appropriate here. (One of these fast things is actually adding something to a job queue.)
TLDR; After receiving the API request, create a new thread and run your function there. You can pass your desired data in kwargs.
The Flask response class, appropriately called Response , is rarely used directly by Flask applications. Instead, Flask uses it internally as a container for the response data returned by application route functions, plus some additional information needed to create an HTTP response.
Basically, the "endpoint" is an identifier that is used in determining what logical unit of your code should handle the request. Normally, an endpoint is just the name of a view function. However, you can actually change the endpoint, as is done in the following example.
Sadly teardown callbacks do not execute after the response has been returned to the client:
import flask import time app = flask.Flask("after_response") @app.teardown_request def teardown(request): time.sleep(2) print("teardown_request") @app.route("/") def home(): return "Success!\n" if __name__ == "__main__": app.run()
When curling this you'll note a 2s delay before the response displays, rather than the curl ending immediately and then a log 2s later. This is further confirmed by the logs:
teardown_request 127.0.0.1 - - [25/Jun/2018 15:41:51] "GET / HTTP/1.1" 200 -
The correct way to execute after a response is returned is to use WSGI middleware that adds a hook to the close method of the response iterator. This is not quite as simple as the teardown_request
decorator, but it's still pretty straight-forward:
import traceback from werkzeug.wsgi import ClosingIterator class AfterResponse: def __init__(self, app=None): self.callbacks = [] if app: self.init_app(app) def __call__(self, callback): self.callbacks.append(callback) return callback def init_app(self, app): # install extension app.after_response = self # install middleware app.wsgi_app = AfterResponseMiddleware(app.wsgi_app, self) def flush(self): for fn in self.callbacks: try: fn() except Exception: traceback.print_exc() class AfterResponseMiddleware: def __init__(self, application, after_response_ext): self.application = application self.after_response_ext = after_response_ext def __call__(self, environ, start_response): iterator = self.application(environ, start_response) try: return ClosingIterator(iterator, [self.after_response_ext.flush]) except Exception: traceback.print_exc() return iterator
Which you can then use like this:
@app.after_response def after(): time.sleep(2) print("after_response")
From the shell you will see the response return immediately and then 2 seconds later the after_response
will hit the logs:
127.0.0.1 - - [25/Jun/2018 15:41:51] "GET / HTTP/1.1" 200 - after_response
This is a summary of a previous answer provided here.
QUICK and EASY method.
We will use pythons Thread Library to acheive this.
Your API consumer has sent something to process and which is processed by my_task() function which takes 10 seconds to execute. But the consumer of the API wants a response as soon as they hit your API which is return_status() function.
You tie the my_task to a thread and then return the quick response to the API consumer, while in the background the big process gets compelete.
Below is a simple POC.
import os from flask import Flask,jsonify import time from threading import Thread app = Flask(__name__) @app.route("/") def main(): return "Welcome!" @app.route('/add_') def return_status(): """Return first the response and tie the my_task to a thread""" Thread(target = my_task).start() return jsonify('Response asynchronosly') def my_task(): """Big function doing some job here I just put pandas dataframe to csv conversion""" time.sleep(10) import pandas as pd pd.DataFrame(['sameple data']).to_csv('./success.csv') return print('large function completed') if __name__ == "__main__": app.run(host="0.0.0.0", port=8080)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With