UniTask Intro
What is UniTask?
- Generic abstraction for async operations
- RPC requests
- Asset loads
- Animations
- Computations (e.g. Jobs)
- ...anything not-sync
- Same idea as Future, Promise, Task
API
struct UniTask<T>
{
// simple callback
public UniTask ContinueWith(Action<T> continuationFunction);
// transformation (aka mapping)
public UniTask<TR> ContinueWith(Func<T,TR> continuationFunction);
public UniTask<TR> ContinueWith(Func<T,UniTask<TR>> continuationFunction);
// await-able with syntax sugar
public Awaiter GetAwaiter();
// wrap into enumerator for coroutine integration
public IEnumerator ToCoroutine();
}
Creation: callback
UniTask<string> LoadData()
{
// create a source "promise"
var source = new UniTaskCompletionSource<string>();
// resolve the promise from a callback (there's also TrySetException/TrySetCanceled)
LoadDataWithCallback(data => source.TrySetResult(data));
// return it outside as a task
return source.Task;
}
Creation: sync result
UniTask<string> LoadData()
{
if (cachedData != null)
{
// there's also FromException/FromCanceled
return UniTask.FromResult(cachedData);
}
return ActuallyLoadData();
}
UniTask<string> ActuallyLoadData { ... }
Creation: async method
async UniTask<string> LoadData()
{
if (cachedData != null) {
return cachedData;
}
return await ActuallyLoadData();
}
UniTask<string> ActuallyLoadData { ... }
Creation: unity coroutine
LoadDataCoro().ToUniTask(monoBehaviourToRunCoroutineOn);
// there's also ToUniTask that hooks into PlayerLoop,
// and doesn't need a monobehaviour, with some limitations
Creation: utilities
UniTask.Delay(ms);
UniTask.NextFrame();
UniTask.DelayFrame(frames);
UniTask.WaitUntil(() => someCondition);
// ...and more
Creation: unity async stuff
Resources.LoadAsync<Sprite>("icon").ToUniTask();
SceneManager.LoadSceneAsync("SomeScene").ToUniTask();
// ...etc
- ResourceRequest/AssetBundleRequest
- AsyncOperation/AsyncOperationHandle
- UnityWebRequestAsyncOperation
- DOTween
(with support for progress reporting and cancellation)
Transformation: ContinueWith
UniTask<Data> LoadData()
{
return LoadJson().ContinueWith(json => JsonUtility.FromJson<Data>(json));
}
UniTask<string> LoadJson() { ... }
Composition: WhenAll
UniTask<(Data, Sprite)> LoadDataAndImage()
{
return UniTask.WhenAll(LoadData(), LoadImage());
}
async/await
- async transforms function into a state-machine
- states are code blocks separated by await
- await suspends execution of async function and yields control to the "awaiter"
- "awaiter" gives control it back to the state machine when it's done
- So almost just like yield (but with return values and more fitting type)
async UniTask LoadWithPrinting()
{
// state 0
print("Loading...");
var data = await LoadData();
// state 1
print("Loading complete: " + data);
}
async/await: awaiter
- supporting abstraction for the await syntax
- anything can be "awaitable" with GetAwaiter
- UniTask has GetAwaiter for various Unity things
- ...even for tuples of tasks (shorthand for WhenAll)
-
var (data, image) = await (LoadData(), LoadImage())
-
Cancellation
// create token and pass it to an asynchronous function
var tokenSource = new CancellationTokenSource();
LoadData(tokenSource.Token).ContinueWith(OnDataLoaded);
// ...cancel later (e.g. if UI was closed before data is loaded)
tokenSource.Cancel();
Hypothetical Complex Example
async UniTask PrepareBattle()
{
StartIntroAnimation();
await (
LoadAssets(GetCommonAssetsIds()),
LoadAssets(GetPlayerAssetsIds()),
FindMatch().ContinueWith(match => LoadAssets(GetOpponentAssetsIds(match))),
UniTask.WaitUntil(() => animationComplete)
);
}
UniTask intro
By Dan Korostelev
UniTask intro
- 248