问题
I have a function that crawls the web for data and computes a score for the search. However, this can take a while and sometimes the webpage times out before finishing execution.
So I created a separate thread that executes the function and loading.html
that tells the client that data is still being collected. Once the function ends in the thread, how do I reload the webpage to display output.html
that displays the score.
This is a simpler version of what I have so far:
from flask import Flask
from flask import render_template
from threading import Thread
app = Flask(__name__)
@app.route("/")
def init():
return render_template('index.html')
@app.route("/", methods=['POST'])
def load():
th = Thread(target=something, args=())
th.start()
return render_template('loading.html')
def something():
#do some calculation and return the needed value
if __name__ == "__main__":
app.run()
How do I route my app to render_template('output.html', x=score)
once something()
inside the thread th
finishes?
I am trying to avoid task queues like redis since I want to deploy this app on the web and I don't want to incur charges (this is more of an experiment and hobby).
A detailed answer with code would help a lot since I am new to flask and multithreading
回答1:
An easy way is making cyclic Ajax requests to a thread_status endpoint that gives you information about the currently running task.
import time
from flask import Flask, jsonify
from flask import render_template
from threading import Thread
app = Flask(__name__)
th = Thread()
finished = False
@app.route("/")
def init():
return render_template('index.html')
@app.route("/", methods=['POST'])
def load():
global th
global finished
finished = False
th = Thread(target=something, args=())
th.start()
return render_template('loading.html')
def something():
""" The worker function """
global finished
time.sleep(5)
finished = True
@app.route('/result')
def result():
""" Just give back the result of your heavy work """
return 'Done'
@app.route('/status')
def thread_status():
""" Return the status of the worker thread """
return jsonify(dict(status=('finished' if finished else 'running')))
if __name__ == "__main__":
app.run(debug=True)
So in your loading.html just insert a cyclic Ajax get()
request:
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script>
$(document).ready(function() {
var refresh_id = setInterval(function() {
$.get(
"{{ url_for('thread_status') }}",
function(data) {
console.log(data);
if (data.status == 'finished') {
window.location.replace("{{ url_for('result') }}");
}
}
)}
, 1000);
});
</script>
</head>
<body>
<p>Loading...</p>
</body>
</html>
You can even append this by a progress counter if you like. But you need to take care that you prevent the thread from being run multiple times.
来源:https://stackoverflow.com/questions/41319199/how-do-i-change-the-rendered-template-in-flask-when-a-thread-completes