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