文档

Unity Engine

Unity Engine

Awaitable 代码示例参考

Solve common asynchronous programming problems with a reference of Awaitable code examples.
阅读时间4 分钟最后更新于 1 个月前

本参考中的示例演示了编写异步代码时遇到的常见情况的
Awaitable
解决方案。

异步测试

Unity 的测试框架无法将
Awaitable
识别为有效的测试返回类型。但是,以下示例展示了如何使用
IEnumerator
的
Awaitable
实现来编写异步测试:
[UnityTest]public IEnumerator SomeAsyncTest(){ async Awaitable TestImplementation(){ // test something with async / await support here }; return TestImplementation();}

框架协同程序

您可以使用
Awaitable
类中与框架相关的异步方法来创建异步 Unity 协同程序,以替代基于迭代器的协同程序:
async Awaitable SampleSchedulingJobsForNextFrame(){ // Wait until end of frame to avoid competing over resources with other Unity subsystems await Awaitable.EndOfFrameAsync(); var jobHandle = ScheduleSomethingWithJobSystem(); // Let the job execute while the next frame starts await Awaitable.NextFrameAsync(); jobHandle.Complete(); // Use results of computation}JobHandle ScheduleSomethingWithJobSystem(){ ...}

协程等待

在基于迭代器的协同程序中,WaitUntil 会暂停协同程序执行,直到委托对
true
求值。您可以使用取消令牌使
Awaitable
返回异步方法等待条件更改,从而为该方法创建等效行为:
public static async AwaitableUntil(Func<bool> condition, CancellationTokenSource cancellationToken){ while(!condition()){ cancellationToken.ThrowIfCancellationRequested(); await Awaitable.NextFrameAsync(); }}
然后,您可以按如下方式传入取消令牌:
cancellationTokenSource = new CancellationTokenSource();currentTask = AwaitableUntil(myCondition, cancellationTokenSource.Token);

异步加载资源

您可以
await
异步资源加载操作,使其不会阻塞主线程:
public async Awaitable Start(){ // Load texture resource asynchronously var operation = Resources.LoadAsync("my-texture"); // Return control to the main thread while the resource loads await operation; var texture = operation.asset as Texture2D;}

编译

您可以在同一方法中
await
多个不同的
await
兼容类型:
public async Awaitable Start(){ await CallSomeThirdPartyAPIReturningDotnetTask(); await Awaitable.NextFrameAsync(); await SceneManager.LoadSceneAsync("my-scene"); await SomeUserCodeReturningAwaitable(); ...}

在 .NET Task 中封装 Awaitable

要解决
Awaitable
的一些限制,可以将其封装在 .NET
Task
中。这会产生分配开销,但允许从
Task
API 访问
WhenAll
和
WhenAny
等方法。为此,您可以编写自己的自定义
AsTask
扩展方法,如下所示:
// Implement custom AsTask extension methods to wrap Awaitable in Taskpublic static class AwaitableExtensions { public static async Task AsTask(this Awaitable a) { await a; } public static async Task<T> AsTask<T>(this Awaitable<T> a) { return await a; } }

多次等待结果

Awaitable
和
Task
之间的主要区别在于,
Awaitable
对象会被池化以减少分配。您无法安全地多次
await
以结果结束的
Awaitable
返回方法,因为一旦返回,原始
Awaitable
对象就会返回到池中。

不安全版本

以下代码不安全,将导致异常和死锁:
async Awaitable Bar(){ var taskWithResult = SomeAwaitableReturningFunction(); var awaitOnce = await taskWithResult; // Do something // The following will cause errors because at this point taskWithResult has already been pooled back var awaitTwice = await taskWithResult; }

安全版本

这种情况下,您能够以分配开销将 Awaitable 封装在 Task 中。然后,可以安全地多次等待
Task
:
// Implement custom AsTask extension methods to wrap Awaitable in Taskpublic static class AwaitableExtensions { public static async Task AsTask(this Awaitable a) { await a; } public static async Task<T> AsTask<T>(this Awaitable<T> a) { return await a; } }async Awaitable Bar(){ var taskWithResult = SomeAwaitableReturningFunction(); // Wrap the returned Awaitable in a Task var taskWithResultAsTask = taskWithResult.AsTask(); // The task can now be safely awaited multiple times, at the cost of allocating var awaitOnce = await taskWithResultAsTask; // Do something var awaitTwice = await taskWithResultAsTask; }

其他资源