Improve document

master
neuecc 2020-03-16 23:36:46 +09:00
parent 50ba93f951
commit 66ba4b9986
1 changed files with 151 additions and 23 deletions

174
README.md
View File

@ -73,11 +73,14 @@ UniTask feature rely on C# 7.0([task-like custom async method builder feature](h
Why UniTask(custom task-like object) is required? Because Task is too heavy, not matched to Unity threading(single-thread). UniTask does not use thread and SynchronizationContext because almost Unity's asynchronous object is automaticaly dispatched by Unity's engine layer. It acquires more fast and more less allocation, completely integrated with Unity. Why UniTask(custom task-like object) is required? Because Task is too heavy, not matched to Unity threading(single-thread). UniTask does not use thread and SynchronizationContext because almost Unity's asynchronous object is automaticaly dispatched by Unity's engine layer. It acquires more fast and more less allocation, completely integrated with Unity.
> More details, please see this slide: [Deep Dive async/await in Unity with UniTask(EN)
](https://www.slideshare.net/neuecc/deep-dive-asyncawait-in-unity-with-unitasken)
You can await `AsyncOperation`, `ResourceRequest`, `UnityWebRequestAsyncOperation`, `IEnumerator` and others when using `UniRx.Async`. You can await `AsyncOperation`, `ResourceRequest`, `UnityWebRequestAsyncOperation`, `IEnumerator` and others when using `UniRx.Async`.
`UniTask.Delay`, `UniTask.Yield`, `UniTask.Timeout` that is frame-based timer operators(no uses thread so works on WebGL publish) driven by custom PlayerLoop(Unity 2018 experimental feature). In default, UniTask initialize automatically when application begin, but it is override all. If you want to append PlayerLoop, please call `PlayerLoopHelper.Initialize(ref yourCustomizedPlayerLoop)` manually. `UniTask.Delay`, `UniTask.Yield`, `UniTask.Timeout` that is frame-based timer operators(no uses thread so works on WebGL publish) driven by custom PlayerLoop(Unity 2018 experimental feature). In default, UniTask initialize automatically when application begin, but it is override all. If you want to append PlayerLoop, please call `PlayerLoopHelper.Initialize(ref yourCustomizedPlayerLoop)` manually.
> Before Unity 2019.3, Unity does not have `PlayerLooop.GetCurrentlayerLoop` so you can't use with Unity ECS package in default. If you want to use with ECS and before Unity 2019.3, you can use this hack below. > Before Unity 2019.3, Unity does not have `PlayerLooop.GetCurrentPlayerLoop` so you can't use with Unity ECS package in default. If you want to use with ECS and before Unity 2019.3, you can use this hack below.
```csharp ```csharp
// Get ECS Loop. // Get ECS Loop.
@ -134,23 +137,135 @@ You can convert Task -> UniTask: `AsUniTask`, `UniTask` -> `UniTask<AsyncUnit>`:
If you want to convert async to coroutine, you can use `UniTask.ToCoroutine`, this is useful to use only allow coroutine system. If you want to convert async to coroutine, you can use `UniTask.ToCoroutine`, this is useful to use only allow coroutine system.
Reusable Promises Cancellation and Exception handling
--- ---
Some UniTask factory methods have `CancellationToken cancellation = default(CancellationToken)` parameter. Andalso some async operation for unity have `ConfigureAwait(..., CancellationToken cancellation = default(CancellationToken))` extension methods.
Exception handling You can pass `CancellationToken` to parameter by standard [`CancellationTokenSource`](https://docs.microsoft.com/en-us/dotnet/api/system.threading.cancellationtokensource).
```csharp
var cts = new CancellationTokenSource();
cancelButton.onClick.AddListener(() =>
{
cts.Cancel();
});
await UnityWebRequest.Get("http://google.co.jp").SendWebRequest().ConfigureAwait(cancellation: cts.Token);
await UniTask.DelayFrame(1000, cancellationToken: cts.Token);
```
When detect cancellation, all methods throws `OperationCanceledException` and propagate to upstream. `OperationCanceledException` is special exception, if not handled this exception, finally it is propagated to `UniTaskScheduler.UnobservedTaskException`.
Default behaviour of received unhandled exception is write log as warning. Log level can change by `UniTaskScheduler.UnobservedExceptionWriteLogType`. If you want to change custom beavhiour, set action to `UniTaskScheduler.UnobservedTaskException.`
If you want to cancel behaviour in async UniTask method, throws `OperationCanceledException` manually.
```csharp
public async UniTask<int> FooAsync()
{
await UniTask.Yield();
throw new OperationCanceledException();
}
```
If you handle exception but want to ignore(propagete to global cancellation handling), use exception filter.
```csharp
public async UniTask<int> BarAsync()
{
try
{
var x = await FooAsync();
return x * 2;
}
catch (Exception ex) when (!(ex is OperationCanceledException))
{
return -1;
}
}
```
throws/catch `OperationCanceledException` is slightly heavy, if you want to care performance, use `UniTask.SuppressCancellationThrow` to avoid OperationCanceledException throw. It returns `(bool IsCanceled, T Result)` instead of throw.
```csharp
var (isCanceled, _) = await UniTask.DelayFrame(10, cancellationToken: cts.Token).SuppressCancellationThrow();
if (isCanceled)
{
// ...
}
```
Note: Only suppress throws if you call it directly into the most source method.
Progress
--- ---
`OperationCanceledException` and `UniTaskScheduler.UnobservedTaskException`, `UniTaskVoid`. and what is `UniTask.SuppressCancellationThrow`. Some async operation for unity have `ConfigureAwait(IProgress<float> progress = null, ...)` extension methods.
```csharp
var progress = Progress.Create<float>(x => Debug.Log(x));
var request = await UnityWebRequest.Get("http://google.co.jp")
.SendWebRequest()
.ConfigureAwait(progress: progress);
```
Should not use `new System.Progress<T>`, because it allocate every times. Use `UniRx.Async.Progress` instead. Progress factory has two methods, `Create` and `CreateOnlyValueChanged`. `CreateOnlyValueChanged` calls only when progress value changed. Should not use `new System.Progress<T>`, it allocate every times.
Implements interface is more better.
```csharp
public class Foo : MonoBehaviour, IProgress<float>
{
public void Report(float value)
{
UnityEngine.Debug.Log(value);
}
public async UniTaskVoid WebRequest()
{
var request = await UnityWebRequest.Get("http://google.co.jp")
.SendWebRequest()
.ConfigureAwait(progress: this); // pass this
}
}
```
UniTaskTracker UniTaskTracker
--- ---
useful for check(leak) UniTasks. useful for check(leak) UniTasks. You can open tracker window in `Window -> UniRx -> UniTask Tracker`.
![](https://user-images.githubusercontent.com/46207/50421527-abf1cf80-0883-11e9-928a-ffcd47b8c454.png) ![](https://user-images.githubusercontent.com/46207/50421527-abf1cf80-0883-11e9-928a-ffcd47b8c454.png)
awaitable Events * Enable AutoReload(Toggle) - Reload automatically.
* Reload - Reload view.
* GC.Collect - Invoke GC.Collect.
* Enable Tracking(Toggle) - Start to track async/await UniTask. Performance impact: low.
* Enable StackTrace(Toggle) - Capture StackTrace when task is started. Performance impact: high.
For debug use, enable tracking and capture stacktrace is useful but it it decline performance. Recommended usage is enable both to find task leak, and when done, finally disable both.
Reusable Promises
--- ---
Some UniTask factory can reuse to reduce allocation. The list is `Yield`, `Delay`, `DelayFrame`, `WaitUntil`, `WaitWhile`, `WaitUntilValueChanged`.
```csharp ```csharp
var reusePromise = UniTask.DelayFrame(10);
for (int i = 0; i < 10; i++)
{
await reusePromise;
}
```
awaitable Events
---
Unity events can await like `OnClickAsync`, `OnCollisionEnterAsync`. It can use by `UniRx.Async.Triggers`.
```csharp
using UniRx.Async.Triggers;
async UniTask TripleClick(CancellationToken token) async UniTask TripleClick(CancellationToken token)
{ {
await button.OnClickAsync(token); await button.OnClickAsync(token);
@ -172,13 +287,41 @@ async UniTask TripleClick(CancellationToken token)
} }
``` ```
async void vs async UniTask/UniTaskVoid
---
`async void` is standard C# system so does not run on UniTask systems. It is better not to use. `async UniTaskVoid` is lightweight version of `async UniTask` because it does not have awaitable completion. If you don't require to await it(fire and forget), use `UniTaskVoid` is better. Unfortunately to dismiss warning, require to using with `Forget()`.
For Unit Testing
---
Unity's `[UnityTest]` attribute can test coroutine(IEnumerator) but can not test async. `UniTask.ToCoroutine` bridges async/await to coroutine so you can test async method.
```csharp
[UnityTest]
public IEnumerator DelayIgnore() => UniTask.ToCoroutine(async () =>
{
var time = Time.realtimeSinceStartup;
Time.timeScale = 0.5f;
try
{
await UniTask.Delay(TimeSpan.FromSeconds(3), ignoreTimeScale: true);
var elapsed = Time.realtimeSinceStartup - time;
Assert.AreEqual(3, (int)Math.Round(TimeSpan.FromSeconds(elapsed).TotalSeconds, MidpointRounding.ToEven));
}
finally
{
Time.timeScale = 1.0f;
}
});
```
Method List Method List
--- ---
```csharp ```csharp
UniTask.WaitUntil UniTask.WaitUntil
UniTask.WaitWhile UniTask.WaitWhile
UniTask.WaitUntilValueChanged UniTask.WaitUntilValueChanged
UniTask.WaitUntilValueChangedWithIsDestroyed
UniTask.SwitchToThreadPool UniTask.SwitchToThreadPool
UniTask.SwitchToTaskPool UniTask.SwitchToTaskPool
UniTask.SwitchToMainThread UniTask.SwitchToMainThread
@ -192,21 +335,6 @@ UniTask.DelayFrame
UniTask.Delay(..., bool ignoreTimeScale = false, ...) parameter UniTask.Delay(..., bool ignoreTimeScale = false, ...) parameter
``` ```
Cancellation
---
async void vs async UniTask/UniTaskVoid
---
Progress
---
For Unit Testing
---
Reference
---
License License
--- ---
This library is under the MIT License. This library is under the MIT License.