mirror of
https://github.com/Cysharp/UniTask.git
synced 2026-05-16 20:20:45 +00:00
complete delay and factory
This commit is contained in:
@@ -10,6 +10,574 @@ using UniRx.Async.Internal;
|
||||
|
||||
namespace UniRx.Async
|
||||
{
|
||||
public static partial class UniTaskExtensions2
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert UniTask -> UniTask[AsyncUnit].
|
||||
/// </summary>
|
||||
public static UniTask2<AsyncUnit> AsAsyncUnitUniTask(this UniTask2 task)
|
||||
{
|
||||
// use implicit conversion
|
||||
return task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert UniTask[T] -> UniTask.
|
||||
/// </summary>
|
||||
public static UniTask2 AsUniTask<T>(this UniTask2<T> task)
|
||||
{
|
||||
// use implicit conversion
|
||||
return task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert Task[T] -> UniTask[T].
|
||||
/// </summary>
|
||||
public static UniTask2<T> AsUniTask<T>(this Task<T> task, bool useCurrentSynchronizationContext = true)
|
||||
{
|
||||
var promise = new UniTaskCompletionSource2<T>();
|
||||
|
||||
task.ContinueWith((x, state) =>
|
||||
{
|
||||
var p = (UniTaskCompletionSource2<T>)state;
|
||||
|
||||
switch (x.Status)
|
||||
{
|
||||
case TaskStatus.Canceled:
|
||||
p.SetCanceled();
|
||||
break;
|
||||
case TaskStatus.Faulted:
|
||||
p.SetException(x.Exception);
|
||||
break;
|
||||
case TaskStatus.RanToCompletion:
|
||||
p.SetResult(x.Result);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}, promise, useCurrentSynchronizationContext ? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Current);
|
||||
|
||||
return promise.Task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert Task -> UniTask.
|
||||
/// </summary>
|
||||
public static UniTask2 AsUniTask(this Task task, bool useCurrentSynchronizationContext = true)
|
||||
{
|
||||
var promise = new UniTaskCompletionSource2();
|
||||
|
||||
task.ContinueWith((x, state) =>
|
||||
{
|
||||
var p = (UniTaskCompletionSource2)state;
|
||||
|
||||
switch (x.Status)
|
||||
{
|
||||
case TaskStatus.Canceled:
|
||||
p.SetCanceled();
|
||||
break;
|
||||
case TaskStatus.Faulted:
|
||||
p.SetException(x.Exception);
|
||||
break;
|
||||
case TaskStatus.RanToCompletion:
|
||||
p.SetResult();
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}, promise, useCurrentSynchronizationContext ? TaskScheduler.FromCurrentSynchronizationContext() : TaskScheduler.Current);
|
||||
|
||||
return promise.Task;
|
||||
}
|
||||
|
||||
public static Task<T> AsTask<T>(this UniTask2<T> task)
|
||||
{
|
||||
try
|
||||
{
|
||||
var awaiter = task.GetAwaiter();
|
||||
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 =>
|
||||
{
|
||||
var (inTcs, inAwaiter) = ((TaskCompletionSource<T>, UniTask2<T>.Awaiter))state;
|
||||
try
|
||||
{
|
||||
var result = inAwaiter.GetResult();
|
||||
inTcs.SetResult(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
inTcs.SetException(ex);
|
||||
}
|
||||
}, (tcs, awaiter));
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Task.FromException<T>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static Task AsTask(this UniTask2 task)
|
||||
{
|
||||
try
|
||||
{
|
||||
var awaiter = task.GetAwaiter();
|
||||
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 =>
|
||||
{
|
||||
var (inTcs, inAwaiter) = ((TaskCompletionSource<object>, UniTask2.Awaiter))state;
|
||||
try
|
||||
{
|
||||
inAwaiter.GetResult();
|
||||
inTcs.SetResult(null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
inTcs.SetException(ex);
|
||||
}
|
||||
}, (tcs, awaiter));
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Task.FromException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerator ToCoroutine<T>(this UniTask2<T> task, Action<T> resultHandler = null, Action<Exception> exceptionHandler = null)
|
||||
{
|
||||
return new ToCoroutineEnumerator<T>(task, resultHandler, exceptionHandler);
|
||||
}
|
||||
|
||||
public static IEnumerator ToCoroutine(this UniTask2 task, Action<Exception> exceptionHandler = null)
|
||||
{
|
||||
return new ToCoroutineEnumerator(task, exceptionHandler);
|
||||
}
|
||||
|
||||
public static UniTask Timeout(this UniTask2 task, TimeSpan timeout, bool ignoreTimeScale = true, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
|
||||
{
|
||||
return Timeout(task.AsAsyncUnitUniTask(), timeout, ignoreTimeScale, timeoutCheckTiming, taskCancellationTokenSource);
|
||||
}
|
||||
|
||||
// TODO: require UniTask2.Delay, WhenAny, etc...
|
||||
|
||||
public static async UniTask<T> Timeout<T>(this UniTask2<T> task, TimeSpan timeout, bool ignoreTimeScale = true, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
|
||||
{
|
||||
// left, right both suppress operation canceled exception.
|
||||
|
||||
var delayCancellationTokenSource = new CancellationTokenSource();
|
||||
var timeoutTask = (UniTask)UniTask.Delay(timeout, ignoreTimeScale, timeoutCheckTiming).SuppressCancellationThrow();
|
||||
|
||||
var (hasValue, value) = await UniTask.WhenAny(task.SuppressCancellationThrow(), timeoutTask);
|
||||
|
||||
if (!hasValue)
|
||||
{
|
||||
if (taskCancellationTokenSource != null)
|
||||
{
|
||||
taskCancellationTokenSource.Cancel();
|
||||
taskCancellationTokenSource.Dispose();
|
||||
}
|
||||
|
||||
throw new TimeoutException("Exceed Timeout:" + timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
delayCancellationTokenSource.Cancel();
|
||||
delayCancellationTokenSource.Dispose();
|
||||
}
|
||||
|
||||
if (value.IsCanceled)
|
||||
{
|
||||
Error.ThrowOperationCanceledException();
|
||||
}
|
||||
|
||||
return value.Result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Timeout with suppress OperationCanceledException. Returns (bool, IsCacneled).
|
||||
/// </summary>
|
||||
public static async UniTask2<bool> TimeoutWithoutException(this UniTask2 task, TimeSpan timeout, bool ignoreTimeScale = true, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
|
||||
{
|
||||
var v = await TimeoutWithoutException(task.AsAsyncUnitUniTask(), timeout, ignoreTimeScale, timeoutCheckTiming, taskCancellationTokenSource);
|
||||
return v.IsTimeout;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Timeout with suppress OperationCanceledException. Returns (bool IsTimeout, T Result).
|
||||
/// </summary>
|
||||
public static async UniTask2<(bool IsTimeout, T Result)> TimeoutWithoutException<T>(this UniTask2<T> task, TimeSpan timeout, bool ignoreTimeScale = true, PlayerLoopTiming timeoutCheckTiming = PlayerLoopTiming.Update, CancellationTokenSource taskCancellationTokenSource = null)
|
||||
{
|
||||
// left, right both suppress operation canceled exception.
|
||||
|
||||
var delayCancellationTokenSource = new CancellationTokenSource();
|
||||
var timeoutTask = (UniTask)UniTask.Delay(timeout, ignoreTimeScale, timeoutCheckTiming).SuppressCancellationThrow();
|
||||
|
||||
var (hasValue, value) = await UniTask.WhenAny(task.SuppressCancellationThrow(), timeoutTask);
|
||||
|
||||
if (!hasValue)
|
||||
{
|
||||
if (taskCancellationTokenSource != null)
|
||||
{
|
||||
taskCancellationTokenSource.Cancel();
|
||||
taskCancellationTokenSource.Dispose();
|
||||
}
|
||||
|
||||
return (true, default(T));
|
||||
}
|
||||
else
|
||||
{
|
||||
delayCancellationTokenSource.Cancel();
|
||||
delayCancellationTokenSource.Dispose();
|
||||
}
|
||||
|
||||
if (value.IsCanceled)
|
||||
{
|
||||
Error.ThrowOperationCanceledException();
|
||||
}
|
||||
|
||||
return (false, value.Result);
|
||||
}
|
||||
|
||||
public static void Forget(this UniTask2 task)
|
||||
{
|
||||
ForgetCore(task).Forget();
|
||||
}
|
||||
|
||||
public static void Forget(this UniTask2 task, Action<Exception> exceptionHandler, bool handleExceptionOnMainThread = true)
|
||||
{
|
||||
if (exceptionHandler == null)
|
||||
{
|
||||
ForgetCore(task).Forget();
|
||||
}
|
||||
else
|
||||
{
|
||||
ForgetCoreWithCatch(task, exceptionHandler, handleExceptionOnMainThread).Forget();
|
||||
}
|
||||
}
|
||||
|
||||
// UniTask to UniTaskVoid
|
||||
static async UniTaskVoid ForgetCore(UniTask2 task)
|
||||
{
|
||||
await task;
|
||||
}
|
||||
|
||||
static async UniTaskVoid ForgetCoreWithCatch(UniTask2 task, Action<Exception> exceptionHandler, bool handleExceptionOnMainThread)
|
||||
{
|
||||
try
|
||||
{
|
||||
await task;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (handleExceptionOnMainThread)
|
||||
{
|
||||
await UniTask2.SwitchToMainThread();
|
||||
}
|
||||
exceptionHandler(ex);
|
||||
}
|
||||
catch (Exception ex2)
|
||||
{
|
||||
UniTaskScheduler.PublishUnobservedTaskException(ex2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Forget<T>(this UniTask2<T> task)
|
||||
{
|
||||
ForgetCore(task).Forget();
|
||||
}
|
||||
|
||||
public static void Forget<T>(this UniTask2<T> task, Action<Exception> exceptionHandler, bool handleExceptionOnMainThread = true)
|
||||
{
|
||||
if (exceptionHandler == null)
|
||||
{
|
||||
ForgetCore(task).Forget();
|
||||
}
|
||||
else
|
||||
{
|
||||
ForgetCoreWithCatch(task, exceptionHandler, handleExceptionOnMainThread).Forget();
|
||||
}
|
||||
}
|
||||
|
||||
// UniTask to UniTaskVoid
|
||||
static async UniTaskVoid ForgetCore<T>(UniTask2<T> task)
|
||||
{
|
||||
await task;
|
||||
}
|
||||
|
||||
static async UniTaskVoid ForgetCoreWithCatch<T>(UniTask2<T> task, Action<Exception> exceptionHandler, bool handleExceptionOnMainThread)
|
||||
{
|
||||
try
|
||||
{
|
||||
await task;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (handleExceptionOnMainThread)
|
||||
{
|
||||
await UniTask.SwitchToMainThread();
|
||||
}
|
||||
exceptionHandler(ex);
|
||||
}
|
||||
catch (Exception ex2)
|
||||
{
|
||||
UniTaskScheduler.PublishUnobservedTaskException(ex2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async UniTask2 ContinueWith<T>(this UniTask2<T> task, Action<T> continuationFunction)
|
||||
{
|
||||
continuationFunction(await task);
|
||||
}
|
||||
|
||||
public static async UniTask2 ContinueWith<T>(this UniTask2<T> task, Func<T, UniTask2> continuationFunction)
|
||||
{
|
||||
await continuationFunction(await task);
|
||||
}
|
||||
|
||||
public static async UniTask2<TR> ContinueWith<T, TR>(this UniTask2<T> task, Func<T, TR> continuationFunction)
|
||||
{
|
||||
return continuationFunction(await task);
|
||||
}
|
||||
|
||||
public static async UniTask2<TR> ContinueWith<T, TR>(this UniTask2<T> task, Func<T, UniTask2<TR>> continuationFunction)
|
||||
{
|
||||
return await continuationFunction(await task);
|
||||
}
|
||||
|
||||
public static async UniTask2 ContinueWith(this UniTask2 task, Action continuationFunction)
|
||||
{
|
||||
await task;
|
||||
continuationFunction();
|
||||
}
|
||||
|
||||
public static async UniTask2 ContinueWith(this UniTask2 task, Func<UniTask2> continuationFunction)
|
||||
{
|
||||
await task;
|
||||
await continuationFunction();
|
||||
}
|
||||
|
||||
public static async UniTask2<T> ContinueWith<T>(this UniTask2 task, Func<T> continuationFunction)
|
||||
{
|
||||
await task;
|
||||
return continuationFunction();
|
||||
}
|
||||
|
||||
public static async UniTask2<T> ContinueWith<T>(this UniTask2 task, Func<UniTask2<T>> continuationFunction)
|
||||
{
|
||||
await task;
|
||||
return await continuationFunction();
|
||||
}
|
||||
|
||||
public static async UniTask2 ConfigureAwait(this Task task, PlayerLoopTiming timing)
|
||||
{
|
||||
await task.ConfigureAwait(false);
|
||||
await UniTask2.Yield(timing);
|
||||
}
|
||||
|
||||
public static async UniTask2<T> ConfigureAwait<T>(this Task<T> task, PlayerLoopTiming timing)
|
||||
{
|
||||
var v = await task.ConfigureAwait(false);
|
||||
await UniTask2.Yield(timing);
|
||||
return v;
|
||||
}
|
||||
|
||||
public static async UniTask2 ConfigureAwait(this UniTask2 task, PlayerLoopTiming timing)
|
||||
{
|
||||
await task;
|
||||
await UniTask2.Yield(timing);
|
||||
}
|
||||
|
||||
public static async UniTask2<T> ConfigureAwait<T>(this UniTask2<T> task, PlayerLoopTiming timing)
|
||||
{
|
||||
var v = await task;
|
||||
await UniTask2.Yield(timing);
|
||||
return v;
|
||||
}
|
||||
|
||||
public static async UniTask2<T> Unwrap<T>(this UniTask2<UniTask2<T>> task)
|
||||
{
|
||||
return await await task;
|
||||
}
|
||||
|
||||
public static async UniTask2 Unwrap<T>(this UniTask2<UniTask2> task)
|
||||
{
|
||||
await await task;
|
||||
}
|
||||
|
||||
class ToCoroutineEnumerator : IEnumerator
|
||||
{
|
||||
bool completed;
|
||||
UniTask2 task;
|
||||
Action<Exception> exceptionHandler = null;
|
||||
bool isStarted = false;
|
||||
ExceptionDispatchInfo exception;
|
||||
|
||||
public ToCoroutineEnumerator(UniTask2 task, Action<Exception> exceptionHandler)
|
||||
{
|
||||
completed = false;
|
||||
this.exceptionHandler = exceptionHandler;
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
async UniTaskVoid RunTask(UniTask2 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)
|
||||
{
|
||||
// throw exception on iterator (main)thread.
|
||||
// unfortunately unity test-runner can not handle throw exception on hand-write IEnumerator.MoveNext.
|
||||
UnityEngine.Debug.LogException(exception.SourceException);
|
||||
}
|
||||
|
||||
return !completed;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class ToCoroutineEnumerator<T> : IEnumerator
|
||||
{
|
||||
bool completed;
|
||||
Action<T> resultHandler = null;
|
||||
Action<Exception> exceptionHandler = null;
|
||||
bool isStarted = false;
|
||||
UniTask2<T> task;
|
||||
object current = null;
|
||||
ExceptionDispatchInfo exception;
|
||||
|
||||
public ToCoroutineEnumerator(UniTask2<T> task, Action<T> resultHandler, Action<Exception> exceptionHandler)
|
||||
{
|
||||
completed = false;
|
||||
this.task = task;
|
||||
this.resultHandler = resultHandler;
|
||||
this.exceptionHandler = exceptionHandler;
|
||||
}
|
||||
|
||||
async UniTaskVoid RunTask(UniTask2<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)
|
||||
{
|
||||
// throw exception on iterator (main)thread.
|
||||
// unfortunately unity test-runner can not handle throw exception on hand-write IEnumerator.MoveNext.
|
||||
UnityEngine.Debug.LogException(exception.SourceException);
|
||||
}
|
||||
|
||||
return !completed;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO:remove
|
||||
public static partial class UniTaskExtensions
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user