Home > Software design >  Anynchronously update a web page using Flask
Anynchronously update a web page using Flask

Time:12-31

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.

  • Related