You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Dash回调执行过程中返回结果,实现加载器动态显隐?

Dash点击按钮立即显示Loader并计算完成后隐藏的实现方案

问题背景

开发Dash应用时,需要实现:用户点击按钮后立即显示Loader,同时执行1-2分钟的耗时计算,计算完成后自动隐藏Loader。但遇到两个问题:

  • 普通回调只能在执行结束后返回结果,无法在计算开始时先更新Loader状态
  • Dash不允许多个回调输出同一组件,无法拆分Loader的显示/隐藏逻辑

之前尝试的问题代码:

@app.callback(
    Output("container", "children"),
    Output("lottie-loader", "style"),  # 初始状态Loader隐藏
    Input("scan-button", "n_clicks"),
    State("container", "children")
)
def show_on_map(n_clicks, children):
    if (n_clicks > 0):  
        style = {'display': 'none'}  # 放在此处避免启动时执行
        
        # 耗时很长的计算
        
    return children, style  # 最终返回隐藏状态


@app.callback(
    Output("lottie-loader", "style"),
    Input("scan-button", "n_clicks"),
)
def update_style(n_clicks):
    if (n_clicks > 0):
        style = {'display': 'block'}  # 尝试让Loader显示
    return style

方案一:使用Dash后台回调(推荐)

后台回调支持耗时任务在后台运行,同时可通过running参数定义任务执行前后的组件状态变化,完美匹配需求。

完整代码

from dash import Dash, html, Input, Output, State, callback
from dash.long_callback import DiskcacheLongCallbackManager
import diskcache

# 初始化后台回调缓存管理器
cache = diskcache.Cache("./cache")
long_callback_manager = DiskcacheLongCallbackManager(cache)

app = Dash(__name__, long_callback_manager=long_callback_manager)

app.layout = html.Div([
    html.Button("开始计算", id="scan-button", n_clicks=0),
    html.Div(id="container"),
    # 替换为你的Lottie Loader组件,初始隐藏
    html.Div(
        "正在计算,请稍候...",
        id="lottie-loader",
        style={"display": "none", "marginTop": "15px", "color": "#666"}
    )
])

@callback(
    Output("container", "children"),
    Output("lottie-loader", "style"),
    Input("scan-button", "n_clicks"),
    State("container", "children"),
    # 定义任务运行前后的组件状态
    running=[
        (Output("scan-button", "disabled"), True, False),  # 运行时禁用按钮,结束后恢复
        (Output("lottie-loader", "style"), {"display": "block"}, {"display": "none"})  # 运行时显示Loader,结束后隐藏
    ],
    prevent_initial_call=True,
    background=True  # 开启后台运行模式
)
def show_on_map(n_clicks, children):
    # 替换为你的实际耗时计算逻辑
    import time
    time.sleep(120)  # 模拟2分钟计算
    
    # 计算完成后返回更新内容和隐藏Loader的样式
    return children + [html.P("✅ 计算完成!")], {"display": "none"}

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

关键说明

  • running参数:直接指定任务启动/结束时的组件状态切换,无需手动在回调中控制
  • background=True:让计算任务在后台执行,不会阻塞前端界面响应
  • prevent_initial_call=True:避免应用启动时误触发回调

方案二:客户端回调+服务器端回调组合

如果不想依赖后台回调,可通过客户端回调立即触发Loader显示,服务器端执行计算后再通知前端隐藏Loader。

完整代码

from dash import Dash, html, dcc, Input, Output, State, callback, clientside_callback

app = Dash(__name__)

app.layout = html.Div([
    html.Button("开始计算", id="scan-button", n_clicks=0),
    html.Div(id="container"),
    # 替换为你的Lottie Loader组件
    html.Div(
        "正在计算,请稍候...",
        id="lottie-loader",
        style={"display": "none", "marginTop": "15px", "color": "#666"}
    ),
    # 用Store存储计算状态,在客户端和服务器端间传递
    dcc.Store(id="calc-status", data="idle")
])

# 客户端回调:点击按钮立即显示Loader并标记计算启动
clientside_callback(
    """
    function(n_clicks) {
        if (n_clicks > 0) {
            return [{"display": "block"}, "running"];
        }
        return [{"display": "none"}, "idle"];
    }
    """,
    Output("lottie-loader", "style"),
    Output("calc-status", "data"),
    Input("scan-button", "n_clicks"),
    prevent_initial_call=True
)

# 服务器端回调:执行耗时计算,完成后更新状态
@callback(
    Output("container", "children"),
    Output("calc-status", "data"),
    Input("calc-status", "data"),
    State("container", "children"),
    prevent_initial_call=True
)
def perform_calculation(status, children):
    if status == "running":
        # 替换为你的实际耗时计算逻辑
        import time
        time.sleep(120)
        
        return children + [html.P("✅ 计算完成!")], "completed"
    return children, status

# 客户端回调:计算完成后隐藏Loader
clientside_callback(
    """
    function(status) {
        if (status === "completed") {
            return {"display": "none"};
        }
        return window.dash_clientside.no_update;
    }
    """,
    Output("lottie-loader", "style"),
    Input("calc-status", "data"),
    prevent_initial_call=True
)

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

关键说明

  • 客户端回调直接在浏览器端执行,无需等待服务器响应,实现Loader的即时显示
  • dcc.Store作为状态中间件,在客户端和服务器端传递计算状态
  • 计算完成后通过状态通知客户端回调隐藏Loader

内容的提问来源于stack exchange,提问作者Ben

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 14:21:00