Display a 'Loading' Message While a Time Consuming Function Is Executed in Flask

Display a ‘loading’ message while a time consuming function is executed in Flask

This can be done by using a div that contains a 'loading gif' image. When the submit button is clicked, the div is displayed using javascript.
To implement this, you can take a look at this website: http://web.archive.org/web/20181023063601/http://www.netavatar.co.in/2011/05/31/how-to-show-a-loading-gif-image-while-a-page-loads-using-javascript-and-css/

Flask - show loading page while another time consuming function is running

Finally I found the solution!

Thanks to Laurel's answer. I'll just make it more nice and clear.

What I've done

I redesigned my Flask app, so it looks like this:

from flask import Flask, render_template
import map_plotting_module as mpm

app = Flask(__name__)

@app.route('/')
def loading():
return render_template("loading.html")

@app.route('/map')
def show_map():
return render_template("map.html")

@app.route('/create_map')
def create_map():
mpm.create_map()
return "Map created"

if __name__ == '__main__':
app.run()

When user opens the page, flask stars rendering the loading.html file.
In this file you have to add the following code to the <head> section:

<script>
function navigate() {
window.location.href = 'map'; // Redirects user to the /map route when 'create_map' is finished
}
fetch('create_map').then(navigate); // Performing 'create_map' and then calls navigate() function, declared above
</script>

Then, add a loading wheel div to your <body> section:

<body>
<div class="loader"></div>
</body>

If it's still not clear for you - please check my example at the end of the answer

Explanation

In the <script> section we have to declare navigate() function. It just redirects the user to the desired reference, /map in the current case.

fetch() is the analog to jQuery.ajax() - read more. It's just fetching the app route to /create_map, awaits it to be done in the "backround", and then performs action in the .then() block - navigate() function in our case.

So the workflow is:

  1. User opens the page, @app.route('/') function is performed, which is loading page.
  2. Loading page fetching @app.route('/create_map') and runs its functions in the background.
  3. When create_map() function is completed - user is being redirected to the @app.route('/map') which function is rendering all-done map.html template.

Few recommendations from me

  • If you want your loading page to have an icon, just add the following tab to your <head> section of loading.html:
<link rel="icon" href="/static/<icon_file>">

Pay attention, Flask is searching for media in the /static folder. Put your media in it. Otherwise, media will not be rendered!

  • If you want your loading page to have a nice CSS loader, consider visiting this page: loading.io. I really enjoyed it.

Finally, here's code snippet as an example of loader:

.loader {
position: absolute;
top: 50%;
left: 50%;
margin: -56px 0 0 -56px;
}

.loader:after {
content: " ";
display: block;
width: 110px;
height: 110px;
border-radius: 50%;
border: 1px solid;
border-color: #0aa13a transparent #47a90e transparent;
animation: ring 1.2s linear infinite;
}

@keyframes ring {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>Loading...</title>
</head>

<body>
<div class="loader"></div>
</body>

</html>

Flask - use javascript to display loading page while waiting for task to complete

Since you're using a GET request, you can have your anchor tag route to cooking, render the cooking.html template, then use window.location.replace() to redirect to pasta. The long task in pasta() will be performed before pasta.html is rendered.

In menu.html:

<a href="{{ url_for('cooking') }}">Pasta</a>

In cooking.html:

<script> window.location.replace('/pasta'); </script>

In the case of a POST request, you would need to send the form values to cooking, pass them to the template, then send them again to pasta using AJAX, and finally redirect in the callback function.

In menu.html:

<form action="{{ url_for('cooking') }}" method='POST'>

In cooking():

@app.route('/cooking')
def cooking():
return render_template('cooking.html', form_data=request.form['form_data'])

In pasta():

@app.route('/pasta', methods=['GET', 'POST'])
def pasta():
if request.method == 'GET':
render_template('pasta.html')

# (perform some long task here)

return make_response('POST request successful', 200)

In cooking.html:

<script>
$.ajax({
type: 'POST',
url: '/pasta',
data: {
form_data: '{{form_data}}',
},
success: function() {
window.location.replace('/pasta');
}
});
</script>

Flask: show contents before time-consuming function completes

As mentioned in comments, you need to make the architecture event-driven by using celery task manager. f() is defined as a task and called async.
Define f() as a task with celery in a specific file (e.x. task.py) like this:

from celery import Celery

# Creating a celery instance with redis as message broker.
app = Celery('my_task', broker='redis://localhost')

@app.task
def f(arguments):
"""
This is a celery task. Just a normal function with task decorator.
Note that we use the decorator from a celery instance.
"""
pass # do f here
# save the result in database or cache and map it to task id

After the abouve definition, you need to import f() from task.py and call it by passing arguments like this:

...the usual flask stuff here...
from task import f # import f function from task.py

@app.route('/compute')
def compute():
result = f.delay(arguments) # call f async like this
return render_template('page.html', computed_result_task_id = result.task_id) # You can't return the result unless wait for the result, so just return the task id and get the result later via another API.
# save task result in a database or cache and map to task_id for further retrieval

It was a brief introduction, use the documentation for details of implementations.

And also, you need to execute the task like this:

celery -A task worker -l ERROR --detach

(There are more options to run and manage a task in documentation.)

How to add a loading gif to the page when a function runs in the background in Flask?

This needs to be done on the client side only.

Add jquery in your header section:

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>

Add this to your submit button code:

onclick="$('#loading').show();"

Get a loading.gif image Add below code after your form in html

<div id="loading" style="display:none;"><img src="loading.gif" alt="Sample Image" />Loading!</div>

References:
1 2



Related Topics



Leave a reply



Submit