Search code examples
c#unity-game-engineasynchronous

How do I make my function correctly use a callback after async functions are completed?


I have this function that loads a number of assets via async and I want to call the onComplete action when all the assets have been loaded but currently given the nature of async I am not sure how to make my function do that as I am relatively new to programming in async.

public void Load(Action onComplete)
{
    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);

            //check if all assets have been loaded and then call onComplete
        };
    }
}

Solution

  • You should use Microsoft's Reactive Framework (aka Rx) - NuGet System.Reactive and add using System.Reactive.Linq; - then you can do this:

    public void Load2(Action onComplete)
    {
        (
            from kvp in _db.ToObservable()
            let key = kvp.Key
            let assetRef = kvp.Value
            let assetLoader = assetRef.LoadAssetAsync<TAsset>()
            from handle in
                Observable
                    .FromEvent<Action<AssetHandle>, AssetHandle>(
                        h => assetLoader.Completed += h,
                        h => assetLoader.Completed -= h)
                    .Take(1)
            where handle.Status == AsyncOperationStatus.Succeeded
            select new { key, handle.Result }
        )
            .Subscribe(
                x => _loadedAssets.Add(x.key, x.Result),
                () => onComplete());
    }
    

    Now, that should work. I had to reverse engineer some types, but I think I worked it out. The race condition still exists in your code.