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

Blazor页面如何检测页面是否被离开,终止长时间运行任务

解决Blazor长任务在页面关闭/刷新时停止的问题

1. 基础方案:覆盖组件卸载与内部导航场景

你已经用到的NavigationManager.RegisterLocationChangingHandler可以处理Blazor应用内的导航跳转,结合IAsyncDisposable接口,能覆盖组件正常卸载的情况:

  • 让组件实现IAsyncDisposable,在DisposeAsync方法中取消长任务的CancellationTokenSource。
  • 注册LocationChangingHandler时,在回调里触发任务取消逻辑。

示例代码:

@implements IAsyncDisposable
@inject NavigationManager NavManager

@code {
    private CancellationTokenSource _cts = new();
    private Task _longRunningTask;

    protected override void OnInitialized()
    {
        // 注册导航变化监听
        NavManager.RegisterLocationChangingHandler(OnLocationChanging);
        base.OnInitialized();
    }

    private async ValueTask OnLocationChanging(LocationChangingContext context)
    {
        // 取消长任务
        _cts.Cancel();
        await _longRunningTask; // 等待任务终止
    }

    // 启动长任务的方法
    public async Task StartLongTask()
    {
        _cts = new CancellationTokenSource();
        _longRunningTask = ExecuteLongTask(_cts.Token);
        await _longRunningTask;
    }

    private async Task ExecuteLongTask(CancellationToken token)
    {
        try
        {
            // 你的下载、合并逻辑,全程传入token并检查取消状态
            while (!token.IsCancellationRequested)
            {
                await DownloadFileFromVendorApi(token);
                await Task.Delay(1000, token);
            }
        }
        catch (OperationCanceledException)
        {
            // 任务被取消,执行资源清理
            CleanupTempFiles();
        }
    }

    public async ValueTask DisposeAsync()
    {
        _cts.Cancel();
        if (_longRunningTask != null)
        {
            await _longRunningTask;
        }
        _cts.Dispose();
    }
}

2. 处理标签页关闭/刷新的场景

方案A:心跳检测(适配Blazor Server)

如果长任务运行在服务器端,可通过前端定时发送心跳,服务器端检测超时心跳来取消任务:

  • 前端组件:用Timer定时调用后端接口更新心跳时间戳。
  • 后端服务:维护任务ID与最后心跳时间的映射,定时扫描超时(比如15秒未收到)的任务并取消。

示例代码(组件端):

private Timer _heartbeatTimer;

protected override void OnAfterRender(bool firstRender)
{
    if (firstRender)
    {
        // 每5秒发送一次心跳
        _heartbeatTimer = new Timer(async _ => await SendHeartbeat(), null, 0, 5000);
    }
    base.OnAfterRender(firstRender);
}

private async Task SendHeartbeat()
{
    if (_longRunningTask != null && !_cts.IsCancellationRequested)
    {
        await Http.PostAsync($"api/tasks/{CurrentTaskId}/heartbeat", null);
    }
}

// 在DisposeAsync中销毁定时器
public async ValueTask DisposeAsync()
{
    _heartbeatTimer?.Dispose();
    // ... 其他清理逻辑
}

方案B:JavaScript事件监听(适配Blazor WASM/Server)

通过JSInterop监听浏览器的pagehide和visibilitychange事件,这些事件在标签页关闭、刷新、切换时会触发,比beforeunload更稳定,且只需少量JS代码:

  1. 在wwwroot/js目录创建pageEvents.js:
export function registerPageUnloadListener(callback) {
    // 监听页面关闭/刷新事件
    document.addEventListener('pagehide', () => {
        callback();
    });
    // 监听标签切换导致的页面隐藏
    document.addEventListener('visibilitychange', () => {
        if (document.hidden) {
            callback();
        }
    });
}
  1. 在组件中引入并调用该JS模块:
@implements IAsyncDisposable
@inject IJSRuntime JSRuntime

@code {
    private IJSObjectReference _jsModule;
    private CancellationTokenSource _cts = new();

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            _jsModule = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./js/pageEvents.js");
            // 注册监听,传入任务取消回调
            await _jsModule.InvokeVoidAsync("registerPageUnloadListener", () => _cts.Cancel());
        }
        await base.OnAfterRenderAsync(firstRender);
    }

    // ... 长任务逻辑同基础方案

    public async ValueTask DisposeAsync()
    {
        _cts.Cancel();
        if (_jsModule != null)
        {
            await _jsModule.DisposeAsync();
        }
        _cts.Dispose();
    }
}

关键注意事项

  • 所有长任务的异步操作必须传入CancellationToken,并在关键步骤检查token.IsCancellationRequested,确保任务能及时终止。
  • Blazor Server场景下,还可实现ICircuitHandler接口,在CircuitClosed方法中直接取消当前用户的所有长任务。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 22:43:27