You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
921 lines
29 KiB
921 lines
29 KiB
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member |
|
|
|
using System; |
|
using System.Collections; |
|
using System.Runtime.ExceptionServices; |
|
using System.Threading; |
|
using System.Threading.Tasks; |
|
using Cysharp.Threading.Tasks.Internal; |
|
|
|
namespace Cysharp.Threading.Tasks |
|
{ |
|
public static partial class UniTaskExtensions |
|
{ |
|
/// <summary> |
|
/// Convert Task[T] -> UniTask[T]. |
|
/// </summary> |
|
public static UniTask<T> AsUniTask<T>(this Task<T> task, bool useCurrentSynchronizationContext = true) |
|
{ |
|
var promise = new UniTaskCompletionSource<T>(); |
|
|
|
task.ContinueWith((x, state) => |
|
{ |
|
var p = (UniTaskCompletionSource<T>)state; |
|
|
|
switch (x.Status) |
|
{ |
|
case TaskStatus.Canceled: |
|
p.TrySetCanceled(); |
|
break; |
|
case TaskStatus.Faulted: |
|
p.TrySetException(x.Exception); |
|
break; |
|
case TaskStatus.RanToCompletion: |
|
p.TrySetResult(x.Result); |
|
break; |
|
default: |
|
throw new NotSupportedException(); |
|
} |
|
}, promise, useCurrentSynchronizationContext ? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Current); |
|
|
|
return promise.Task; |
|
} |
|
|
|
/// <summary> |
|
/// Convert Task -> UniTask. |
|
/// </summary> |
|
public static UniTask AsUniTask(this Task task, bool useCurrentSynchronizationContext = true) |
|
{ |
|
var promise = new UniTaskCompletionSource(); |
|
|
|
task.ContinueWith((x, state) => |
|
{ |
|
var p = (UniTaskCompletionSource)state; |
|
|
|
switch (x.Status) |
|
{ |
|
case TaskStatus.Canceled: |
|
p.TrySetCanceled(); |
|
break; |
|
case TaskStatus.Faulted: |
|
p.TrySetException(x.Exception); |
|
break; |
|
case TaskStatus.RanToCompletion: |
|
p.TrySetResult(); |
|
break; |
|
default: |
|
throw new NotSupportedException(); |
|
} |
|
}, promise, useCurrentSynchronizationContext ? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Current); |
|
|
|
return promise.Task; |
|
} |
|
|
|
public static Task<T> AsTask<T>(this UniTask<T> task) |
|
{ |
|
try |
|
{ |
|
UniTask<T>.Awaiter awaiter; |
|
try |
|
{ |
|
awaiter = task.GetAwaiter(); |
|
} |
|
catch (Exception ex) |
|
{ |
|
return Task.FromException<T>(ex); |
|
} |
|
|
|
if (awaiter.IsCompleted) |
|
{ |
|
try |
|
{ |
|
var result = awaiter.GetResult(); |
|
return Task.FromResult(result); |
|
} |
|
catch (Exception ex) |
|
{ |
|
return Task.FromException<T>(ex); |
|
} |
|
} |
|
|
|
var tcs = new TaskCompletionSource<T>(); |
|
|
|
awaiter.SourceOnCompleted(state => |
|
{ |
|
using (var tuple = (StateTuple<TaskCompletionSource<T>, UniTask<T>.Awaiter>)state) |
|
{ |
|
var (inTcs, inAwaiter) = tuple; |
|
try |
|
{ |
|
var result = inAwaiter.GetResult(); |
|
inTcs.SetResult(result); |
|
} |
|
catch (Exception ex) |
|
{ |
|
inTcs.SetException(ex); |
|
} |
|
} |
|
}, StateTuple.Create(tcs, awaiter)); |
|
|
|
return tcs.Task; |
|
} |
|
catch (Exception ex) |
|
{ |
|
return Task.FromException<T>(ex); |
|
} |
|
} |
|
|
|
public static Task AsTask(this UniTask task) |
|
{ |
|
try |
|
{ |
|
UniTask.Awaiter awaiter; |
|
try |
|
{ |
|
awaiter = task.GetAwaiter(); |
|
} |
|
catch (Exception ex) |
|
{ |
|
return Task.FromException(ex); |
|
} |
|
|
|
if (awaiter.IsCompleted) |
|
{ |
|
try |
|
{ |
|
awaiter.GetResult(); // check token valid on Succeeded |
|
return Task.CompletedTask; |
|
} |
|
catch (Exception ex) |
|
{ |
|
return Task.FromException(ex); |
|
} |
|
} |
|
|
|
var tcs = new TaskCompletionSource<object>(); |
|
|
|
awaiter.SourceOnCompleted(state => |
|
{ |
|
using (var tuple = (StateTuple<TaskCompletionSource<object>, UniTask.Awaiter>)state) |
|
{ |
|
var (inTcs, inAwaiter) = tuple; |
|
try |
|
{ |
|
inAwaiter.GetResult(); |
|
inTcs.SetResult(null); |
|
} |
|
catch (Exception ex) |
|
{ |
|
inTcs.SetException(ex); |
|
} |
|
} |
|
}, StateTuple.Create(tcs, awaiter)); |
|
|
|
return tcs.Task; |
|
} |
|
catch (Exception ex) |
|
{ |
|
return Task.FromException(ex); |
|
} |
|
} |
|
|
|
public static AsyncLazy ToAsyncLazy(this UniTask task) |
|
{ |
|
return new AsyncLazy(task); |
|
} |
|
|
|
public static AsyncLazy<T> ToAsyncLazy<T>(this UniTask<T> task) |
|
{ |
|
return new AsyncLazy<T>(task); |
|
} |
|
|
|
/// <summary> |
|
/// Ignore task result when cancel raised first. |
|
/// </summary> |
|
public static UniTask AttachExternalCancellation(this UniTask task, CancellationToken cancellationToken) |
|
{ |
|
if (!cancellationToken.CanBeCanceled) |
|
{ |
|
return task; |
|
} |
|
|
|
if (cancellationToken.IsCancellationRequested) |
|
{ |
|
return UniTask.FromCanceled(cancellationToken); |
|
} |
|
|
|
if (task.Status.IsCompleted()) |
|
{ |
|
return task; |
|
} |
|
|
|
return new UniTask(new AttachExternalCancellationSource(task, cancellationToken), 0); |
|
} |
|
|
|
/// <summary> |
|
/// Ignore task result when cancel raised first. |
|
/// </summary> |
|
public static UniTask<T> AttachExternalCancellation<T>(this UniTask<T> task, CancellationToken cancellationToken) |
|
{ |
|
if (!cancellationToken.CanBeCanceled) |
|
{ |
|
return task; |
|
} |
|
|
|
if (cancellationToken.IsCancellationRequested) |
|
{ |
|
return UniTask.FromCanceled<T>(cancellationToken); |
|
} |
|
|
|
if (task.Status.IsCompleted()) |
|
{ |
|
return task; |
|
} |
|
|
|
return new UniTask<T>(new AttachExternalCancellationSource<T>(task, cancellationToken), 0); |
|
} |
|
|
|
sealed class AttachExternalCancellationSource : IUniTaskSource |
|
{ |
|
static readonly Action<object> cancellationCallbackDelegate = CancellationCallback; |
|
|
|
CancellationToken cancellationToken; |
|
CancellationTokenRegistration tokenRegistration; |
|
UniTaskCompletionSourceCore<AsyncUnit> core; |
|
|
|
public AttachExternalCancellationSource(UniTask task, CancellationToken cancellationToken) |
|
{ |
|
this.cancellationToken = cancellationToken; |
|
this.tokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallbackDelegate, this); |
|
RunTask(task).Forget(); |
|
} |
|
|
|
async UniTaskVoid RunTask(UniTask task) |
|
{ |
|
try |
|
{ |
|
await task; |
|
core.TrySetResult(AsyncUnit.Default); |
|
} |
|
catch (Exception ex) |
|
{ |
|
core.TrySetException(ex); |
|
} |
|
finally |
|
{ |
|
tokenRegistration.Dispose(); |
|
} |
|
} |
|
|
|
static void CancellationCallback(object state) |
|
{ |
|
var self = (AttachExternalCancellationSource)state; |
|
self.core.TrySetCanceled(self.cancellationToken); |
|
} |
|
|
|
public void GetResult(short token) |
|
{ |
|
core.GetResult(token); |
|
} |
|
|
|
public UniTaskStatus GetStatus(short token) |
|
{ |
|
return core.GetStatus(token); |
|
} |
|
|
|
public void OnCompleted(Action<object> continuation, object state, short token) |
|
{ |
|
core.OnCompleted(continuation, state, token); |
|
} |
|
|
|
public UniTaskStatus UnsafeGetStatus() |
|
{ |
|
return core.UnsafeGetStatus(); |
|
} |
|
} |
|
|
|
sealed class AttachExternalCancellationSource<T> : IUniTaskSource<T> |
|
{ |
|
static readonly Action<object> cancellationCallbackDelegate = CancellationCallback; |
|
|
|
CancellationToken cancellationToken; |
|
CancellationTokenRegistration tokenRegistration; |
|
UniTaskCompletionSourceCore<T> core; |
|
|
|
public AttachExternalCancellationSource(UniTask<T> task, CancellationToken cancellationToken) |
|
{ |
|
this.cancellationToken = cancellationToken; |
|
this.tokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallbackDelegate, this); |
|
RunTask(task).Forget(); |
|
} |
|
|
|
async UniTaskVoid RunTask(UniTask<T> task) |
|
{ |
|
try |
|
{ |
|
core.TrySetResult(await task); |
|
} |
|
catch (Exception ex) |
|
{ |
|
core.TrySetException(ex); |
|
} |
|
finally |
|
{ |
|
tokenRegistration.Dispose(); |
|
} |
|
} |
|
|
|
static void CancellationCallback(object state) |
|
{ |
|
var self = (AttachExternalCancellationSource<T>)state; |
|
self.core.TrySetCanceled(self.cancellationToken); |
|
} |
|
|
|
void IUniTaskSource.GetResult(short token) |
|
{ |
|
core.GetResult(token); |
|
} |
|
|
|
public T GetResult(short token) |
|
{ |
|
return core.GetResult(token); |
|
} |
|
|
|
public UniTaskStatus GetStatus(short token) |
|
{ |
|
return core.GetStatus(token); |
|
} |
|
|
|
public void OnCompleted(Action<object> continuation, object state, short token) |
|
{ |
|
core.OnCompleted(continuation, state, token); |
|
} |
|
|
|
public UniTaskStatus UnsafeGetStatus() |
|
{ |
|
return core.UnsafeGetStatus(); |
|
} |
|
} |
|
|
|
#if UNITY_2018_3_OR_NEWER |
|
|
|
public static IEnumerator ToCoroutine<T>(this UniTask<T> task, Action<T> resultHandler = null, Action<Exception> exceptionHandler = null) |
|
{ |
|
return new ToCoroutineEnumerator<T>(task, resultHandler, exceptionHandler); |
|
} |
|
|
|
public static IEnumerator ToCoroutine(this UniTask task, Action<Exception> exceptionHandler = null) |
|
{ |
|
return new ToCoroutineEnumerator(task, exceptionHandler); |
|
} |
|
|
|
public static async UniTask Timeout(this UniTask task, TimeSpan timeout, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null) |
|
{ |
|
var delayCancellationTokenSource = new CancellationTokenSource(); |
|
var timeoutTask = UniTask.Delay(timeout, delayType, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow(); |
|
|
|
int winArgIndex; |
|
bool taskResultIsCanceled; |
|
try |
|
{ |
|
(winArgIndex, taskResultIsCanceled, _) = await UniTask.WhenAny(task.SuppressCancellationThrow(), timeoutTask); |
|
} |
|
catch |
|
{ |
|
delayCancellationTokenSource.Cancel(); |
|
delayCancellationTokenSource.Dispose(); |
|
throw; |
|
} |
|
|
|
// timeout |
|
if (winArgIndex == 1) |
|
{ |
|
if (taskCancellationTokenSource != null) |
|
{ |
|
taskCancellationTokenSource.Cancel(); |
|
taskCancellationTokenSource.Dispose(); |
|
} |
|
|
|
throw new TimeoutException("Exceed Timeout:" + timeout); |
|
} |
|
else |
|
{ |
|
delayCancellationTokenSource.Cancel(); |
|
delayCancellationTokenSource.Dispose(); |
|
} |
|
|
|
if (taskResultIsCanceled) |
|
{ |
|
Error.ThrowOperationCanceledException(); |
|
} |
|
} |
|
|
|
public static async UniTask<T> Timeout<T>(this UniTask<T> task, TimeSpan timeout, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null) |
|
{ |
|
var delayCancellationTokenSource = new CancellationTokenSource(); |
|
var timeoutTask = UniTask.Delay(timeout, delayType, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow(); |
|
|
|
int winArgIndex; |
|
(bool IsCanceled, T Result) taskResult; |
|
try |
|
{ |
|
(winArgIndex, taskResult, _) = await UniTask.WhenAny(task.SuppressCancellationThrow(), timeoutTask); |
|
} |
|
catch |
|
{ |
|
delayCancellationTokenSource.Cancel(); |
|
delayCancellationTokenSource.Dispose(); |
|
throw; |
|
} |
|
|
|
// timeout |
|
if (winArgIndex == 1) |
|
{ |
|
if (taskCancellationTokenSource != null) |
|
{ |
|
taskCancellationTokenSource.Cancel(); |
|
taskCancellationTokenSource.Dispose(); |
|
} |
|
|
|
throw new TimeoutException("Exceed Timeout:" + timeout); |
|
} |
|
else |
|
{ |
|
delayCancellationTokenSource.Cancel(); |
|
delayCancellationTokenSource.Dispose(); |
|
} |
|
|
|
if (taskResult.IsCanceled) |
|
{ |
|
Error.ThrowOperationCanceledException(); |
|
} |
|
|
|
return taskResult.Result; |
|
} |
|
|
|
/// <summary> |
|
/// Timeout with suppress OperationCanceledException. Returns (bool, IsCacneled). |
|
/// </summary> |
|
public static async UniTask<bool> TimeoutWithoutException(this UniTask task, TimeSpan timeout, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null) |
|
{ |
|
var delayCancellationTokenSource = new CancellationTokenSource(); |
|
var timeoutTask = UniTask.Delay(timeout, delayType, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow(); |
|
|
|
int winArgIndex; |
|
bool taskResultIsCanceled; |
|
try |
|
{ |
|
(winArgIndex, taskResultIsCanceled, _) = await UniTask.WhenAny(task.SuppressCancellationThrow(), timeoutTask); |
|
} |
|
catch |
|
{ |
|
delayCancellationTokenSource.Cancel(); |
|
delayCancellationTokenSource.Dispose(); |
|
return true; |
|
} |
|
|
|
// timeout |
|
if (winArgIndex == 1) |
|
{ |
|
if (taskCancellationTokenSource != null) |
|
{ |
|
taskCancellationTokenSource.Cancel(); |
|
taskCancellationTokenSource.Dispose(); |
|
} |
|
|
|
return true; |
|
} |
|
else |
|
{ |
|
delayCancellationTokenSource.Cancel(); |
|
delayCancellationTokenSource.Dispose(); |
|
} |
|
|
|
if (taskResultIsCanceled) |
|
{ |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
/// <summary> |
|
/// Timeout with suppress OperationCanceledException. Returns (bool IsTimeout, T Result). |
|
/// </summary> |
|
public static async UniTask<(bool IsTimeout, T Result)> TimeoutWithoutException<T>(this UniTask<T> task, TimeSpan timeout, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null) |
|
{ |
|
var delayCancellationTokenSource = new CancellationTokenSource(); |
|
var timeoutTask = UniTask.Delay(timeout, delayType, timeoutCheckTiming, delayCancellationTokenSource.Token).SuppressCancellationThrow(); |
|
|
|
int winArgIndex; |
|
(bool IsCanceled, T Result) taskResult; |
|
try |
|
{ |
|
(winArgIndex, taskResult, _) = await UniTask.WhenAny(task.SuppressCancellationThrow(), timeoutTask); |
|
} |
|
catch |
|
{ |
|
delayCancellationTokenSource.Cancel(); |
|
delayCancellationTokenSource.Dispose(); |
|
return (true, default); |
|
} |
|
|
|
// timeout |
|
if (winArgIndex == 1) |
|
{ |
|
if (taskCancellationTokenSource != null) |
|
{ |
|
taskCancellationTokenSource.Cancel(); |
|
taskCancellationTokenSource.Dispose(); |
|
} |
|
|
|
return (true, default); |
|
} |
|
else |
|
{ |
|
delayCancellationTokenSource.Cancel(); |
|
delayCancellationTokenSource.Dispose(); |
|
} |
|
|
|
if (taskResult.IsCanceled) |
|
{ |
|
return (true, default); |
|
} |
|
|
|
return (false, taskResult.Result); |
|
} |
|
|
|
#endif |
|
|
|
public static void Forget(this UniTask task) |
|
{ |
|
var awaiter = task.GetAwaiter(); |
|
if (awaiter.IsCompleted) |
|
{ |
|
try |
|
{ |
|
awaiter.GetResult(); |
|
} |
|
catch (Exception ex) |
|
{ |
|
UniTaskScheduler.PublishUnobservedTaskException(ex); |
|
} |
|
} |
|
else |
|
{ |
|
awaiter.SourceOnCompleted(state => |
|
{ |
|
using (var t = (StateTuple<UniTask.Awaiter>)state) |
|
{ |
|
try |
|
{ |
|
t.Item1.GetResult(); |
|
} |
|
catch (Exception ex) |
|
{ |
|
UniTaskScheduler.PublishUnobservedTaskException(ex); |
|
} |
|
} |
|
}, StateTuple.Create(awaiter)); |
|
} |
|
} |
|
|
|
public static void Forget(this UniTask task, Action<Exception> exceptionHandler, bool handleExceptionOnMainThread = true) |
|
{ |
|
if (exceptionHandler == null) |
|
{ |
|
Forget(task); |
|
} |
|
else |
|
{ |
|
ForgetCoreWithCatch(task, exceptionHandler, handleExceptionOnMainThread).Forget(); |
|
} |
|
} |
|
|
|
static async UniTaskVoid ForgetCoreWithCatch(UniTask task, Action<Exception> exceptionHandler, bool handleExceptionOnMainThread) |
|
{ |
|
try |
|
{ |
|
await task; |
|
} |
|
catch (Exception ex) |
|
{ |
|
try |
|
{ |
|
if (handleExceptionOnMainThread) |
|
{ |
|
#if UNITY_2018_3_OR_NEWER |
|
await UniTask.SwitchToMainThread(); |
|
#endif |
|
} |
|
exceptionHandler(ex); |
|
} |
|
catch (Exception ex2) |
|
{ |
|
UniTaskScheduler.PublishUnobservedTaskException(ex2); |
|
} |
|
} |
|
} |
|
|
|
public static void Forget<T>(this UniTask<T> task) |
|
{ |
|
var awaiter = task.GetAwaiter(); |
|
if (awaiter.IsCompleted) |
|
{ |
|
try |
|
{ |
|
awaiter.GetResult(); |
|
} |
|
catch (Exception ex) |
|
{ |
|
UniTaskScheduler.PublishUnobservedTaskException(ex); |
|
} |
|
} |
|
else |
|
{ |
|
awaiter.SourceOnCompleted(state => |
|
{ |
|
using (var t = (StateTuple<UniTask<T>.Awaiter>)state) |
|
{ |
|
try |
|
{ |
|
t.Item1.GetResult(); |
|
} |
|
catch (Exception ex) |
|
{ |
|
UniTaskScheduler.PublishUnobservedTaskException(ex); |
|
} |
|
} |
|
}, StateTuple.Create(awaiter)); |
|
} |
|
} |
|
|
|
public static void Forget<T>(this UniTask<T> task, Action<Exception> exceptionHandler, bool handleExceptionOnMainThread = true) |
|
{ |
|
if (exceptionHandler == null) |
|
{ |
|
task.Forget(); |
|
} |
|
else |
|
{ |
|
ForgetCoreWithCatch(task, exceptionHandler, handleExceptionOnMainThread).Forget(); |
|
} |
|
} |
|
|
|
static async UniTaskVoid ForgetCoreWithCatch<T>(UniTask<T> task, Action<Exception> exceptionHandler, bool handleExceptionOnMainThread) |
|
{ |
|
try |
|
{ |
|
await task; |
|
} |
|
catch (Exception ex) |
|
{ |
|
try |
|
{ |
|
if (handleExceptionOnMainThread) |
|
{ |
|
#if UNITY_2018_3_OR_NEWER |
|
await UniTask.SwitchToMainThread(); |
|
#endif |
|
} |
|
exceptionHandler(ex); |
|
} |
|
catch (Exception ex2) |
|
{ |
|
UniTaskScheduler.PublishUnobservedTaskException(ex2); |
|
} |
|
} |
|
} |
|
|
|
public static async UniTask ContinueWith<T>(this UniTask<T> task, Action<T> continuationFunction) |
|
{ |
|
continuationFunction(await task); |
|
} |
|
|
|
public static async UniTask ContinueWith<T>(this UniTask<T> task, Func<T, UniTask> continuationFunction) |
|
{ |
|
await continuationFunction(await task); |
|
} |
|
|
|
public static async UniTask<TR> ContinueWith<T, TR>(this UniTask<T> task, Func<T, TR> continuationFunction) |
|
{ |
|
return continuationFunction(await task); |
|
} |
|
|
|
public static async UniTask<TR> ContinueWith<T, TR>(this UniTask<T> task, Func<T, UniTask<TR>> continuationFunction) |
|
{ |
|
return await continuationFunction(await task); |
|
} |
|
|
|
public static async UniTask ContinueWith(this UniTask task, Action continuationFunction) |
|
{ |
|
await task; |
|
continuationFunction(); |
|
} |
|
|
|
public static async UniTask ContinueWith(this UniTask task, Func<UniTask> continuationFunction) |
|
{ |
|
await task; |
|
await continuationFunction(); |
|
} |
|
|
|
public static async UniTask<T> ContinueWith<T>(this UniTask task, Func<T> continuationFunction) |
|
{ |
|
await task; |
|
return continuationFunction(); |
|
} |
|
|
|
public static async UniTask<T> ContinueWith<T>(this UniTask task, Func<UniTask<T>> continuationFunction) |
|
{ |
|
await task; |
|
return await continuationFunction(); |
|
} |
|
|
|
public static async UniTask<T> Unwrap<T>(this UniTask<UniTask<T>> task) |
|
{ |
|
return await await task; |
|
} |
|
|
|
public static async UniTask Unwrap(this UniTask<UniTask> task) |
|
{ |
|
await await task; |
|
} |
|
|
|
public static async UniTask<T> Unwrap<T>(this Task<UniTask<T>> task) |
|
{ |
|
return await await task; |
|
} |
|
|
|
public static async UniTask<T> Unwrap<T>(this Task<UniTask<T>> task, bool continueOnCapturedContext) |
|
{ |
|
return await await task.ConfigureAwait(continueOnCapturedContext); |
|
} |
|
|
|
public static async UniTask Unwrap(this Task<UniTask> task) |
|
{ |
|
await await task; |
|
} |
|
|
|
public static async UniTask Unwrap(this Task<UniTask> task, bool continueOnCapturedContext) |
|
{ |
|
await await task.ConfigureAwait(continueOnCapturedContext); |
|
} |
|
|
|
public static async UniTask<T> Unwrap<T>(this UniTask<Task<T>> task) |
|
{ |
|
return await await task; |
|
} |
|
|
|
public static async UniTask<T> Unwrap<T>(this UniTask<Task<T>> task, bool continueOnCapturedContext) |
|
{ |
|
return await (await task).ConfigureAwait(continueOnCapturedContext); |
|
} |
|
|
|
public static async UniTask Unwrap(this UniTask<Task> task) |
|
{ |
|
await await task; |
|
} |
|
|
|
public static async UniTask Unwrap(this UniTask<Task> task, bool continueOnCapturedContext) |
|
{ |
|
await (await task).ConfigureAwait(continueOnCapturedContext); |
|
} |
|
|
|
#if UNITY_2018_3_OR_NEWER |
|
|
|
sealed class ToCoroutineEnumerator : IEnumerator |
|
{ |
|
bool completed; |
|
UniTask task; |
|
Action<Exception> exceptionHandler = null; |
|
bool isStarted = false; |
|
ExceptionDispatchInfo exception; |
|
|
|
public ToCoroutineEnumerator(UniTask task, Action<Exception> exceptionHandler) |
|
{ |
|
completed = false; |
|
this.exceptionHandler = exceptionHandler; |
|
this.task = task; |
|
} |
|
|
|
async UniTaskVoid RunTask(UniTask task) |
|
{ |
|
try |
|
{ |
|
await task; |
|
} |
|
catch (Exception ex) |
|
{ |
|
if (exceptionHandler != null) |
|
{ |
|
exceptionHandler(ex); |
|
} |
|
else |
|
{ |
|
this.exception = ExceptionDispatchInfo.Capture(ex); |
|
} |
|
} |
|
finally |
|
{ |
|
completed = true; |
|
} |
|
} |
|
|
|
public object Current => null; |
|
|
|
public bool MoveNext() |
|
{ |
|
if (!isStarted) |
|
{ |
|
isStarted = true; |
|
RunTask(task).Forget(); |
|
} |
|
|
|
if (exception != null) |
|
{ |
|
exception.Throw(); |
|
return false; |
|
} |
|
|
|
return !completed; |
|
} |
|
|
|
void IEnumerator.Reset() |
|
{ |
|
} |
|
} |
|
|
|
sealed class ToCoroutineEnumerator<T> : IEnumerator |
|
{ |
|
bool completed; |
|
Action<T> resultHandler = null; |
|
Action<Exception> exceptionHandler = null; |
|
bool isStarted = false; |
|
UniTask<T> task; |
|
object current = null; |
|
ExceptionDispatchInfo exception; |
|
|
|
public ToCoroutineEnumerator(UniTask<T> task, Action<T> resultHandler, Action<Exception> exceptionHandler) |
|
{ |
|
completed = false; |
|
this.task = task; |
|
this.resultHandler = resultHandler; |
|
this.exceptionHandler = exceptionHandler; |
|
} |
|
|
|
async UniTaskVoid RunTask(UniTask<T> task) |
|
{ |
|
try |
|
{ |
|
var value = await task; |
|
current = value; // boxed if T is struct... |
|
if (resultHandler != null) |
|
{ |
|
resultHandler(value); |
|
} |
|
} |
|
catch (Exception ex) |
|
{ |
|
if (exceptionHandler != null) |
|
{ |
|
exceptionHandler(ex); |
|
} |
|
else |
|
{ |
|
this.exception = ExceptionDispatchInfo.Capture(ex); |
|
} |
|
} |
|
finally |
|
{ |
|
completed = true; |
|
} |
|
} |
|
|
|
public object Current => current; |
|
|
|
public bool MoveNext() |
|
{ |
|
if (!isStarted) |
|
{ |
|
isStarted = true; |
|
RunTask(task).Forget(); |
|
} |
|
|
|
if (exception != null) |
|
{ |
|
exception.Throw(); |
|
return false; |
|
} |
|
|
|
return !completed; |
|
} |
|
|
|
void IEnumerator.Reset() |
|
{ |
|
} |
|
} |
|
|
|
#endif |
|
} |
|
} |
|
|
|
|