Unity3dAsyncAwaitUtil
Unity3dAsyncAwaitUtil copied to clipboard
Should TaskExtensions.AsIEnumerator unwrap AggregateException?
I'm trying to incrementally convert our codebase from coroutines to async/await. Doing it incrementally means a lot of "await
One issue I've found is that converting a Coroutine -> async Task is not as simple as changing the method from "IEnumerator DoSomethingCoroutine()" -> "async Task DoSomethingAsync()" and fixing up callers to append ".AsIEnumerator()", because exceptions that propagate from Task and AsIEnumerator() are wrapped with AggregateException().
I'm assuming that the main use case of Task.AsIEnumerator is to help with the transition process, so I think there's a good argument for unwrapping the AggregateException. On the other hand, I'm not sure when the AggregateException might contain 2 or more exceptions and what AsIEnumerator should do in that case.
Here's some sample code that illustrates the issue:
class MyCustomException : Exception {}
IEnumerator<object> CallerCoroutine() {
// Old code is sometimes verbose, for call stacks and exception propagation
using (var cr = DoSomethingCoroutine()) {
while (true) {
try {
if (!cr.MoveNext()) { break; }
} catch (MyCustomException e) {
// ...
}
yield return cr.Current;
}
}
// Simple automated refactor doesn't work because the exception is now AggregateException
using (var cr = DoSomethingAsync().AsIEnumerator()) {
// ... body is same as above ...
}
}
IEnumerator<object> DoSomethingCoroutine() {
if (UnityEngine.Random.value > .5f) { throw new MyCustomException(); }
yield return null;
}
async Task<object> DoSomethingAsync() {
if (UnityEngine.Random.value > .5f) { throw new MyCustomException(); }
await Awaiters.NextFrame;
return null;
}