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

如何在多资产异步加载完成后,让函数正确调用onComplete回调

解决异步批量加载资产后触发回调的问题

方法一:用计数器跟踪加载进度(新手友好)

这种方式逻辑直观,容易理解,核心是用线程安全的计数器统计已完成的加载任务,当计数等于总任务数时触发回调。

修改后的代码:

public void Load(Action onComplete)
{
    // 没有资产要加载时直接触发完成
    if (_db.Count == 0)
    {
        onComplete?.Invoke();
        return;
    }

    int completedCount = 0;
    int totalAssets = _db.Count;

    foreach (var kvp in _db)
    {
        var key = kvp.Key;
        var assetRef = kvp.Value;
        assetRef.LoadAssetAsync<TAsset>().Completed += handle =>
        {
            if (handle.Status == AsyncOperationStatus.Succeeded)
                _loadedAssets.Add(key, handle.Result);

            // 用Interlocked保证多线程下计数准确
            int current = Interlocked.Increment(ref completedCount);
            if (current == totalAssets)
            {
                onComplete?.Invoke();
            }
        };
    }
}

关键点说明:

  • 用Interlocked.Increment而不是普通的completedCount++:因为异步回调可能在不同线程执行,普通递增不是线程安全的,会导致计数错误。
  • 先判断空集合:避免无意义的循环,直接触发完成回调。

方法二:用Task.WhenAll简化异步逻辑(推荐)

如果你的项目支持async/await(Unity 2017.1及以上版本已支持),可以把每个加载操作转为Task,用Task.WhenAll等待全部完成,代码更简洁易读。

修改后的代码:

public async void Load(Action onComplete)
{
    var loadTasks = new List<Task>();
    foreach (var kvp in _db)
    {
        var key = kvp.Key;
        var assetRef = kvp.Value;
        // 把LoadAssetAsync转成Task
        loadTasks.Add(LoadAssetTask(assetRef, key));
    }

    // 等待所有加载任务结束
    await Task.WhenAll(loadTasks);
    // 全部完成后触发回调
    onComplete?.Invoke();
}

// 辅助方法:将AssetReference的异步加载转为Task
private Task LoadAssetTask(AssetReference assetRef, string key)
{
    var taskSource = new TaskCompletionSource<bool>();
    assetRef.LoadAssetAsync<TAsset>().Completed += handle =>
    {
        if (handle.Status == AsyncOperationStatus.Succeeded)
            _loadedAssets.Add(key, handle.Result);
        
        // 标记任务完成(不管成功失败)
        taskSource.SetResult(true);
    };
    return taskSource.Task;
}

关键点说明:

  • TaskCompletionSource用来把基于事件的异步操作(Completed事件)转换成Task,这样就能用await语法等待。
  • async void适合作为入口方法或事件处理方法,如果是普通业务方法,建议用async Task返回任务,方便上层处理。

额外注意事项:

  • 如果需要处理加载失败的情况,可以在回调里判断handle.Status == AsyncOperationStatus.Failed,记录错误信息;方法二中也可以用taskSource.SetException抛出异常,上层用try/catch捕获。
  • 如果_loadedAssets是普通Dictionary,多线程下可能出现并发写入问题,建议换成ConcurrentDictionary保证线程安全。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 11:22:48