✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
⚡️ Concerned about the grid? Kyle Baranko teaches how to predicting peak loads using XGBoost. Register for the August webinar!

Returning partial results?

Hi all! Is there any support for partial/intermediate results in Dash?

In particular, I have a long-running computation and a short-running computation, and I’d like to update some output with the fast result and then later update with the slow result.

Intuitively, it seems like I can accomplish the relevant data sharing using a Hidden Div. But, as far as I can tell, Dash requires the full callback graph to finish before the primary callback is called (rather than calling the callback twice with two different triggers). Here was my attempt in 50 lines of code: https://gist.github.com/levon003/f7d3e43851601b478b469415898da5b6

If you don’t want to view the gist, here’s the code directly:

Sample app using a hidden div
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
from datetime import datetime
import time

app = dash.Dash(__name__)

app.layout = html.Div([
    html.H6("Partial results demo"),
    html.Div(["Input: ", dcc.Input(
        id='input', 
        value='',
        placeholder='Enter a query',
        type='text')]),
    html.Br(),
    html.Div(id='output'),
    html.Div(id='intermediate', style={'display': 'none'})

])

@app.callback(
    Output(component_id='output', component_property='children'),
    [Input(component_id='input', component_property='value'), Input(component_id='intermediate', component_property='children')],
    [State(component_id='output', component_property='children')]
)
def update_output_div(input_value, intermediate_value, existing_children):
    print(dash.callback_context, datetime.now())
    if not dash.callback_context.triggered:
        raise PreventUpdate
    trigger_ids = [triggered['prop_id'].split('.')[0] for triggered in dash.callback_context.triggered]
    trigger_str = str(dash.callback_context.triggered)
    
    if existing_children is None:
        existing_children = ['Output:', html.Br()]
    
    new_children = []
    
    if 'input' in trigger_ids:
        # fast computation, return immediately
        new_children.extend([f"Fast result at {datetime.now()} (trigger: {trigger_str})", html.Br()])
    if 'intermediate' in trigger_ids:
        # the slow computation finished! add it to the output
        new_children.extend([intermediate_value, f"(trigger: {trigger_str})", html.Br()])
    return existing_children + new_children

@app.callback(
    Output('intermediate', 'children'),
    Input('input', 'value')
)
def update_intermediate_div(input_value):
    if input_value.strip() == '':
        raise PreventUpdate
    s = datetime.now()
    time.sleep(5)
    long_running_result = f"Slow result at {datetime.now()} after {datetime.now() - s} "
    return long_running_result

if __name__ == '__main__':
    app.run_server(debug=True)

And here’s the output I see:

And here’s the (expected) callback graph:

What I want is for update_output_div to execute once and add the fast result, than execute again once update_intermediate_div terminates with the slow result. But, instead update_output_div doesn’t execute until the slow result is done computing.

First, is my understanding correct that there’s no way to get update_output_div to execute twice in this situation? Second, is my understanding correct that there’s no way to do partial or intermediate output in Dash? Any help appreciated.