Using async / await with task I created using TaskCompletionSource

When my plain C# class method is called, I want it to return a Task object created by the TaskCompletionSource so that the callers of this method can “await” on it. And in this method I will kick start a Unity animation by setting some variable in Animator to true. On the last frame of this animation, there will be an Animation Event that calls a method in my MonoBehaviour script. With this callback trigger at some point I will .SetResult() on the task object I created earlier so that the code that is “await” on it can fall through and continue executing.

When I explained this to someone experienced with Unity, that person flipped out and said that is not safe at all. He was saying that the Task Parallel Library (TPL) may choose to use non-main thread to continue the yielded await point. Is this true? Do I really have to use callback chains to achieve async programming with Unity?

Traditionally in Unity you use coroutines, which are single-threaded faux “multithreading” constructs done with generator functions (IEnumerators).

This satisfies the original constraint that 99% of what you do against Unity must happen in the main thread.

Currently Unity has the jobs system and a whole manner of other async stuff that is in modern C# and making its way to Unity. I am not aware of what that stuff’s current state is, or what the current limitations of it are.

I do know that coroutines will work all day long for 100% of what you commonly need to do in Unity, including networking, animation callbacks, pretty much anything async-y.

I also know that every time I have seen somebody try multthreading in Unity, it has not been pretty.

Good luck.

1 Like

Ok I see. I would have thought when it comes to what thread TPL chooses to continue your await would be a concern of Mono .Net runtime framework not Unity. That Mono should gurantee continuing on the single main thread otherwise doesnt make sense why its Mono in the first place.

I think you are right, I can just use generator to continue code from within the animation callback. Thanks. Completely forgot about generators after working with JS async / await a lot in my day job lol.

Cool… I highly recommend just sticking with coroutines since they’re so dead-simple.

But if you should find you NEED to be in another thread, there is this interesting package:

Caveat: I haven’t used it, but only because I haven’t had a need. Reading the documentation, it appears to let you run coroutines in either the main thread or a background thread.

Oh no, I don’t need to be in another thread at all. I was just wondering if it is safe to use async / await keywords in latest Unity where you can manually resolve / fulfill the task you created and let the caller who is “awaiting” on it to fall through and continue executing. I was hoping this all happens all in main thread. No intentions to multi-thread.

I tried it and had problems with it, errors that I could never figure out.

Tasks work fine with Unity, I only use them to save data in the background at runtime though. Everything else can be done with coroutines with less overhead and garbage generation.

There is https://assetstore.unity.com/packages/tools/animation/more-effective-coroutines-free-54975 for a faster alternative to built in coroutines, with more features. I have used this and it works pretty good.

I apologize to bring this up again but, I’m trying to do more or less the same but for different purposes.
I have this API that calls iCloud to check for saved games:

As you can see the call has a callback made with an Action:

And I would like to have a Task I can await and act in accordance to what happened so I tried this:

Unfortunately, with some calls of this API it works but with some others the call simply doesn’t get awaited or the original thread that called the task doesn’t continue after call it.

Is it because what Kurt-Dekker said about the multithreading problems? Is not like I need it to do it this way but there’s a bunch of calls I need to do to different places and I end up having a callback nightmare with this method.

So, my question… am I doing wrong trying to create tasks out of this calls? if I am, what’s my best option to keep me away from the callback storm shit that awaits me using coroutines and Action callbacks?

Thank you guys!!

1 Like

@pistoleta did you ever figure this out?
found this thread having exact same issues with Stan’s Assets async calls, what a coincidence haha

@manlymooning I gave up by trying to make them async, I dont think it’s possible, I really tried but wasn’t successful, still have the doubt if it’s possible to do. Im using coroutines and the callbacks and as you can imagine the logic turns quite messy.
Btw users from Stan’s asset are quite angry, check the forum, they are not giving any support since April, lots are moving to other assets.

Sorry to bring this back up @pistoleta , but I think the problem above is you were trying to await the task when you were returning it. I use tcs often for callbacks to other services like playfab. You would want to await this from some other async method.

public static Task<bool> DeletePlayerTitleData(List<string> keys)
        {
            var tcs = new TaskCompletionSource<bool>();
            PlayFabClientAPI.UpdateUserData(new UpdateUserDataRequest()
            {
                KeysToRemove = keys
            },
            success =>
            {
                tcs.SetResult(true);
            },
            failure =>
            {
                tcs.SetResult(false);
            });

            return tcs.Task;
        }

public async void DeleteSomeData(string fileName)
        {
            await PlayfabGeneral.DeletePlayerTitleData(new List<string>() { fileName});
        }
2 Likes

Thanks a lot, yeah, I didnt know very well what I was doing. Still I didn’t look how to solve it so thanks a lot for your answer even if late!!!