The Awaitable
class is a custom Unity type that can be awaited and used as an async return type in the C# asynchronous programming model. Most of Unity’s asynchronous APIs support the async
and await
pattern, including:
NextFrameAsync
, WaitForSecondsAsync
, EndOfFrameAsync
, FixedUpdateAsync
AsyncOperation
You can use the Awaitable
class with both the await
operator and as an async
return type in your own code, as follows:
async Awaitable<List<Achievement>> GetAchievementsAsync()
{
var apiResult = await SomeMethodReturningATask(); // or any await-compatible type
List<Achievement> achievements = JsonConvert.DeserializeObject<List<Achievement>>(apiResult);
return achievements;
}
async Awaitable ShowAchievementsView()
{
ShowLoadingOverlay();
List<Achievement> achievements = await GetAchievementsAsync();
HideLoadingOverlay();
ShowAchivementsList(achievements);
}
Awaitable
is designed to offer a more efficient alternative to .NET Task
for asynchronous code in Unity projects. The efficiency of Awaitable
comes with some important limitations compared to Task
.
The most significant limitation is that Awaitable
instances are pooled to limit allocations. Consider the following example:
class SomeMonoBehaviorWithAwaitable : MonoBehavior
{
public async void Start()
{
while(true)
{
// do some work on each frame
await Awaitable.NextFrameAsync();
}
}
}
Without pooling, each instance of the MonoBehavior
in this example would allocate an Awaitable
object each frame, increasing garbage collector workload and degrading performance. To mitigate this, Unity returns the Awaitable
object to the internal Awaitable
pool once it’s been awaited.
Important: The pooling of Awaitable
instances means it’s never safe to await
more than once on an Awaitable
instance. Doing so can result in undefined behavior such as an exception or a deadlock.
The .NET ValueTask<TResult>
offers some of the same key benefits and limitations of Awaitable
. The typical recommended use for ValueTask
is for asynchronous workloads that are expected to complete synchronously most of the time. For more information, refer to Understanding the Whys, Whats, and Whens of ValueTask.
The following table summarizes the feature comparison between Unity’s Awaitable
class and .NET Task
and ValueTask
:
Feature | Task |
ValueTask |
UnityEngine.Awaitable |
---|---|---|---|
Required allocations |
Many. Allocates on every call to a Task -returning method, increasing memory use and garbage collector workload. |
As-needed. Can be optimized with pooling. |
Minimal as-needed. Calling an Awaitable -returning method usually doesn’t allocate memory, since Awaitable instances are pooled by default. |
Safe to await multiple times | Yes. |
No. Must convert to a Task with ValueTask.AsTask . |
No. Must convert to a Task with custom AsTask extension methods, refer to Awaiting multiple times in the same method in the code examples reference. |
Continuations run asynchronously |
Yes. Using the synchronization context by default, otherwise using the ThreadPool . This increases latency when completing on the main thread in Unity because code must wait until the next frame Update to resume. |
Yes. Optimized for the case where awaited tasks complete synchronously. If they complete asynchronously, the continuation behavior is equivalent to Task . |
No. Continuation runs synchronously when completion is triggered, meaning code resumes immediately in the same frame in which completion is triggered. Refer to Awaitable completion and continuation for more information. |
Completion can be triggered by code |
Yes. Using TaskCompletionSource . |
Not applicable in the typical use case, which is for tasks that mostly complete synchronously. |
Yes. Using AwaitableCompletionSource . |
Can return a value |
Yes. Using Task<TResult> . |
Yes. Using ValueTask<TResult> . |
Yes. Using UnityEngine.Awaitable<T> . |
Built-in support for WaitAll and WaitAny |
Yes. |
No. Must convert to Task with ValueTask.AsTask . |
No. Must convert to a Task with custom AsTask extension methods, refer to Wrapping Awaitable in .NET Task in the code examples reference. |
Unity thread and update loop-aware execution scheduling | No. | No. |
Yes. You can specify which thread an Awaitable resumes on with Awaitable.BackgroundThreadAsync and Awaitable.MainThreadAsync . You can also schedule work relative to the Update or FixedUpdate loops with Awaitable.NextFrameAsync and Awaitable.FixedUpdateAsync . For more information, refer to Awaitable completion and continuation. |
The choice of API depends on the performance profile of your asynchronous code, but in general:
Task
is the only choice when you need to await multiple times or from several consumers concurrently.ValueTask
is a good choice if you have high-throughput asynchronous code that completes synchronously most of the time.Awaitable
is a good choice when:
Awaitable
coroutines are usually more efficient than iterator-based coroutines, especially for cases where the iterator returns non-null values, such as WaitForFixedUpdate
.
However, the performance advantage of Awaitable
coroutines reduces when you run many of them concurrently. For example, a MonoBehaviour such as the one in the previous code example, which awaits Awaitable.NextFrameAsync
in a while
loop, is likely to cause performance problems if attached to every GameObjectThe fundamental object in Unity scenes, which can represent characters, props, scenery, cameras, waypoints, and more. A GameObject’s functionality is defined by the Components attached to it. More info
See in Glossary in a large project.
Did you find this page useful? Please give it a rating:
Thanks for rating this page!
What kind of problem would you like to report?
Thanks for letting us know! This page has been marked for review based on your feedback.
If you have time, you can provide more information to help us fix the problem faster.
Provide more information
You've told us this page needs code samples. If you'd like to help us further, you could provide a code sample, or tell us about what kind of code sample you'd like to see:
You've told us there are code samples on this page which don't work. If you know how to fix it, or have something better we could use instead, please let us know:
You've told us there is information missing from this page. Please tell us more about what's missing:
You've told us there is incorrect information on this page. If you know what we should change to make it correct, please tell us:
You've told us this page has unclear or confusing information. Please tell us more about what you found unclear or confusing, or let us know how we could make it clearer:
You've told us there is a spelling or grammar error on this page. Please tell us what's wrong:
You've told us this page has a problem. Please tell us more about what's wrong:
Thank you for helping to make the Unity documentation better!
Your feedback has been submitted as a ticket for our documentation team to review.
We are not able to reply to every ticket submitted.