UniTask, a new async/await library for Unity.
I’ve now released new library to GitHub.
While this is renewed, it is not new so to speak, as it was originally announced as a function for UniRx which has now been made into a separate item. UniRx has been updated alongside this, and with this separation they are now independent from each other, with absolutely no interdependence.
I have made an overview in the following slides.
C#/Unity asynchronous processing for async/await
Asynchronous processing can generally be implemented using call back to to bring up the method after completion. It is often used in Unity as well, but
- Complex processing creates multiple nests
- During this, internal exclusions are not transmitted externally
- You can no longer see the processing order from the code
Such problems result in what is known as call back hell.
In place of this, Unity provides asynchronous processing through coroutines done with yield return (generator).
This coroutine asynchronous processing on Unity has the benefit of being able to be written sequentially, but at the same time
- It combines with MonoBehaviour during startup
- Transmits the return value
- Transmits exceptions
- Controls multiple coroutines (serial/parallel processing)
To mutually supplement the above deficiencies, write the long complex processes with coroutines, transmit the values through call backs, and try to prevent the nests from becoming deep as much as possible. This is the way we have dealt with this until now.
You can incorporate UniRx to lessen the complexity of this kind of asynchronous processing, but
- Its functions are too powerful, which tends to create puzzle-like complexity
- You cannot tell the difference between events (∞ length) and asynchronity (1 in length) with just `IObervable<T>`
- You cannot write it procedurally, making descriptions less easy to understand
These side effects prevent it from becoming to silver bullet.
async/await is designed to be able to “write asynchronous processing like synchronous processing” as a function of the language, which makes it possible to write matrices like the following to distinguish its usage from Rx.
Processing the single return values of synchronization is done by simply calling up the method, but the single return values of asynchronization only requires you to write await there. And like you would process multiple return values for synchronization using foreach, doing event processing in Rx allows you to distinguish naturally between uses of the two.
AsyncEnumerable incorporated through > C# 8.0 makes asynchronous foreach possible. It is different from Rx in that AsyncEnumerable can do Pull asynchronous sequence processing, and IObservable can do Push asynchronous sequence processing.
By using async/await
- You can write procedurally and nests disappear
- You can naturally handle return values, exception processing, and synchronous processing
You can now make descriptions in an ideal manner. However, no processing can exist without side effects, and async/await has the following drawbacks
- The method to call up all asynchronous processes all become Tasks (or something equivalent)
UniTask and Unity
Unity already supports the latest version of C# and can make use of async/await, but there is no support on the framework side, meaning you cannot make practical use of it as is. Several expansions have been implemented with the purpose of making it possible to do what you could do with coroutines on UniTask.
It is built in such a way that you can async/await anything you want as soon as you install UniTask.
UniTask vs Task
You can use `Task<T>` as a return value for async/await in standard C#, but to increase compatibility between performance and Unity on UniTask, I am using the Unity specific, lighter `UniTask<T>`, rather than the standard Task, which is more general purpose. I made this a reality through the implementation of `AsyncMethodBuilder`, which was added in C# 7.0
- do not use ExecutionContext/SynchronizationContext (this reduces overhead)
- use Zero allocation for the value type when internal synchronization has completed
- use an original method group resembling Unity coroutines, which expand PlayerLoop
- prevent leaks using an original expansion of the tracking window
Through the above points, it offers much more in the way of merits when compared to using UniTask without async/await.
Summary
You can use async/await without any restrictions now that async/await support is standard after the official release of Unity 2018.3. However, there are likely still only a small number who are actually making good use of it. I would be very pleased if UniTask can assist people in its implementation.
By the way, Cysharp is the company I established (a subsidiary of Cygames); we develop things like MagicOnion and UniTask withthe purpose of unifying .NET Core and Unity. I am working to construct a maintenance system as a “company”.
With regards to UniRx, which is slow to update, I am thinking of transferring it through moving its community base, so please look out for that as well.