I want to write a Flask JavaScript based program that has two files app.py
and index.html
where we will run a lengthy task that takes one minue to finish and when the task finishes it will automatically update the index.html
page asynchronously.
So I wrote:
app.py
from flask import Flask, render_template
from threading import Thread
import time
app = Flask(__name__)
task_complete = False
def run_lengthy_task():
# Perform the lengthy task here
time.sleep(60) # simulate a task that takes 1 minute to complete
global task_complete
task_complete = True
def fetch_data():
# Fetch the updated data here and return it
return "Updated data"
@app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run()
index.html
<html>
<head>
<script>
function startTask() {
// Start the lengthy task in a separate thread
const thread = new Thread(runLengthyTask);
thread.start();
}
function updatePage() {
// Check the status of the task
if (taskComplete) {
// The task has completed, so fetch the updated data and update the page
document.getElementById("data").innerHTML = "Updated data";
} else {
// The task is still running, so set a timer to check again in a few seconds
setTimeout(updatePage, 1000);
}
}
</script>
</head>
<body>
<button onclick="startTask()">Start Task</button>
<div id="data">
<!-- The data will be displayed here once the task finishes -->
</div>
</body>
</html>
Unfortunately, this doesn't do what I want.
How can I fix the source code?
CodePudding user response:
There are a few ways to do this. To get the sort of interaction that you are looking for without a lot of hassle, you may want to look at websockets.
Flask can be extended to support these with the flask_socketio package. Here is the server-side code to expose a function that just waits a specified number of seconds and then returns some data.
app.py
from flask import Flask, render_template
from flask_socketio import SocketIO, emit, disconnect
from time import sleep
async_mode = None
app = Flask(__name__)
socket_ = SocketIO(app, async_mode=async_mode)
@app.route('/')
def index():
return render_template('index.html',
sync_mode=socket_.async_mode)
@socket_.on('do_task', namespace='/test')
def run_lengthy_task(data):
try:
duration = int(data['duration'])
sleep(duration)
emit('task_done', {'data': 'long task of {} seconds complete'.format(duration)})
disconnect()
except Exception as ex:
print(ex)
if __name__ == '__main__':
socket_.run(app, host='0.0.0.0', port=80, debug=True)
You will see that we are listening for a call to an event of do_task
and we send an event of task_done
when we are finished waiting. We pass messages in both events, the one we receive will contain a duration to wait and then we include a message in the notification that the task is done.
The client-side code for working with this is:
index.html
<!DOCTYPE HTML>
<html>
<head>
<title>Long task</title>
<script src="https://code.jquery.com/jquery-3.6.3.js"></script>
<script src="https://cdn.socket.io/4.5.4/socket.io.min.js"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
namespace = '/test';
var socket = io(namespace);
socket.on('connect', function() {
$('#messages').append('<br/>' $('<div/>').text('Requesting task to run').html());
socket.emit('do_task', {duration: '60'});
});
socket.on('task_done', function(msg, cb) {
$('#messages').append('<br/>' $('<div/>').text(msg.data).html());
if (cb)
cb();
});
});
</script>
</head>
<body>
<h3>Messages</h3>
<div id="messages" ></div>
</body>
</html>
We use SocketIO to make a connection to the server. Once it connects, we send an event of do_task
with our time to wait. When the server sends the event task_done
back, we display the message from it.