mirror of
https://github.com/Cysharp/UniTask.git
synced 2026-05-16 12:00:10 +00:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5f607204b | ||
|
|
676aba7cce | ||
|
|
745de32006 | ||
|
|
a0ef75e797 | ||
|
|
dda6cbd679 | ||
|
|
141b447b30 | ||
|
|
8e7a89eff7 | ||
|
|
7848f878d7 | ||
|
|
c3a02ffbe6 | ||
|
|
d509317ed2 | ||
|
|
74738ab13e | ||
|
|
de3d491375 | ||
|
|
139f5f7914 | ||
|
|
5e2a4934dd | ||
|
|
4ef15f3a14 | ||
|
|
6d56f8d2bb | ||
|
|
6e441a2d14 | ||
|
|
66ba4b9986 | ||
|
|
7e26573abb | ||
|
|
9db6482f0a | ||
|
|
9c257e44f0 | ||
|
|
ecd678117e | ||
|
|
ebce2e922d | ||
|
|
e67562b823 | ||
|
|
d639283f59 | ||
|
|
ecf6c1fba5 |
@@ -6,6 +6,9 @@ executors:
|
|||||||
version: {type: string}
|
version: {type: string}
|
||||||
docker:
|
docker:
|
||||||
- image: gableroux/unity3d:<< parameters.version >>
|
- image: gableroux/unity3d:<< parameters.version >>
|
||||||
|
go:
|
||||||
|
docker:
|
||||||
|
- image: circleci/golang
|
||||||
commands:
|
commands:
|
||||||
unity_activate:
|
unity_activate:
|
||||||
parameters:
|
parameters:
|
||||||
@@ -57,6 +60,17 @@ jobs:
|
|||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: ./UniRx.Async.unitypackage
|
path: ./UniRx.Async.unitypackage
|
||||||
destination: /UniRx.Async.unitypackage
|
destination: /UniRx.Async.unitypackage
|
||||||
|
# upload to github by ghr
|
||||||
|
upload-github:
|
||||||
|
executor: go
|
||||||
|
steps:
|
||||||
|
- attach_workspace:
|
||||||
|
at: .
|
||||||
|
- run: go get github.com/tcnksm/ghr
|
||||||
|
- run: ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} ${CIRCLE_TAG} .
|
||||||
|
- store_artifacts:
|
||||||
|
path: UniRx.Async.unitypackage
|
||||||
|
destination: UniRx.Async.unitypackage
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
version: 2
|
||||||
build-unity:
|
build-unity:
|
||||||
@@ -71,9 +85,15 @@ workflows:
|
|||||||
- build-and-create-package:
|
- build-and-create-package:
|
||||||
unity_version: 2019.1.2f1
|
unity_version: 2019.1.2f1
|
||||||
unity_license: ${UNITY_LICENSE_2019_1}
|
unity_license: ${UNITY_LICENSE_2019_1}
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only: /.*/
|
||||||
- build-and-test:
|
- build-and-test:
|
||||||
unity_version: 2019.1.2f1
|
unity_version: 2019.1.2f1
|
||||||
unity_license: ${UNITY_LICENSE_2019_1}
|
unity_license: ${UNITY_LICENSE_2019_1}
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only: /.*/
|
||||||
# test asmdef will not found.
|
# test asmdef will not found.
|
||||||
# - build-and-test:
|
# - build-and-test:
|
||||||
# unity_version: 2018.4.0f1
|
# unity_version: 2018.4.0f1
|
||||||
@@ -81,4 +101,20 @@ workflows:
|
|||||||
# # UniTask minimum support version is 2018.3(C# 7.x)
|
# # UniTask minimum support version is 2018.3(C# 7.x)
|
||||||
# - build-and-test:
|
# - build-and-test:
|
||||||
# unity_version: 2018.3.12f1
|
# unity_version: 2018.3.12f1
|
||||||
# unity_license: ${UNITY_LICENSE_2018_3}
|
# unity_license: ${UNITY_LICENSE_2018_3}
|
||||||
|
- build-and-create-package-release:
|
||||||
|
unity_version: 2019.1.2f1
|
||||||
|
unity_license: ${UNITY_LICENSE_2019_1}
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only: /^\d\.\d\.\d.*/
|
||||||
|
branches:
|
||||||
|
ignore: /.*/
|
||||||
|
- upload-github:
|
||||||
|
requires:
|
||||||
|
- build-and-create-package-release
|
||||||
|
filters:
|
||||||
|
tags:
|
||||||
|
only: /^\d\.\d\.\d.*/
|
||||||
|
branches:
|
||||||
|
ignore: /.*/
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -145,3 +145,5 @@ RuntimeUnitTestToolkit\.csproj
|
|||||||
Assembly-CSharp-Editor\.csproj
|
Assembly-CSharp-Editor\.csproj
|
||||||
|
|
||||||
UniRx\.Async\.unitypackage
|
UniRx\.Async\.unitypackage
|
||||||
|
|
||||||
|
UniRx.Async.Tests.Editor.csproj
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public static class PackageExporter
|
|||||||
|
|
||||||
var path = Path.Combine(Application.dataPath, root);
|
var path = Path.Combine(Application.dataPath, root);
|
||||||
var assets = Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories)
|
var assets = Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories)
|
||||||
.Where(x => Path.GetExtension(x) == ".cs" || Path.GetExtension(x) == ".asmdef" || Path.GetExtension(x) == ".json")
|
.Where(x => Path.GetExtension(x) == ".cs" || Path.GetExtension(x) == ".asmdef" || Path.GetExtension(x) == ".json" || Path.GetExtension(x) == ".meta")
|
||||||
.Select(x => "Assets" + x.Replace(Application.dataPath, "").Replace(@"\", "/"))
|
.Select(x => "Assets" + x.Replace(Application.dataPath, "").Replace(@"\", "/"))
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
|
|||||||
@@ -169,21 +169,21 @@ Switch,
|
|||||||
SaveSettings(settings);
|
SaveSettings(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MenuItem("Test/Settings/BuildTarget/StandaloneLinux", validate = true, priority = 3)]
|
//[MenuItem("Test/Settings/BuildTarget/StandaloneLinux", validate = true, priority = 3)]
|
||||||
static bool ValidateBuildTargetStandaloneLinux()
|
//static bool ValidateBuildTargetStandaloneLinux()
|
||||||
{
|
//{
|
||||||
Menu.SetChecked("Test/Settings/BuildTarget/StandaloneLinux", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.StandaloneLinux);
|
// Menu.SetChecked("Test/Settings/BuildTarget/StandaloneLinux", LoadOrGetDefaultSettings().BuildTarget == BuildTarget.StandaloneLinux);
|
||||||
return true;
|
// return true;
|
||||||
}
|
//}
|
||||||
|
|
||||||
[MenuItem("Test/Settings/BuildTarget/StandaloneLinux", validate = false, priority = 3)]
|
//[MenuItem("Test/Settings/BuildTarget/StandaloneLinux", validate = false, priority = 3)]
|
||||||
static void BuildTargetStandaloneLinux()
|
//static void BuildTargetStandaloneLinux()
|
||||||
{
|
//{
|
||||||
var settings = LoadOrGetDefaultSettings();
|
// var settings = LoadOrGetDefaultSettings();
|
||||||
settings.UseCurrentBuildTarget = false;
|
// settings.UseCurrentBuildTarget = false;
|
||||||
settings.BuildTarget = BuildTarget.StandaloneLinux;
|
// settings.BuildTarget = BuildTarget.StandaloneLinux;
|
||||||
SaveSettings(settings);
|
// SaveSettings(settings);
|
||||||
}
|
//}
|
||||||
|
|
||||||
[MenuItem("Test/Settings/BuildTarget/StandaloneLinux64", validate = true, priority = 4)]
|
[MenuItem("Test/Settings/BuildTarget/StandaloneLinux64", validate = true, priority = 4)]
|
||||||
static bool ValidateBuildTargetStandaloneLinux64()
|
static bool ValidateBuildTargetStandaloneLinux64()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using UniRx.Async;
|
using UniRx.Async;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@@ -15,19 +16,11 @@ public class SandboxMain : MonoBehaviour
|
|||||||
|
|
||||||
async void Start()
|
async void Start()
|
||||||
{
|
{
|
||||||
await UniTask.CompletedTask; // ok
|
UnityEngine.Debug.Log("DOWNLOAD START:" + Time.frameCount);
|
||||||
|
|
||||||
// var subject = new Subject<Unit>();
|
var req = await UnityWebRequest.Get(Path.Combine(Application.streamingAssetsPath, "test.txt")).SendWebRequest();
|
||||||
//subject.OnCompleted();
|
|
||||||
IObservable<AsyncUnit> subject = default;
|
UnityEngine.Debug.Log("DOWNLOAD RESULT:" + Time.frameCount + ", " + req.downloadHandler.text);
|
||||||
try
|
|
||||||
{
|
|
||||||
await subject.ToUniTask(); // exception
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Debug.Log(exception);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
8
Assets/StreamingAssets.meta
Normal file
8
Assets/StreamingAssets.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7bb6ff9f1f0d0cd4da5ba7623f10dd6b
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
1
Assets/StreamingAssets/test.txt
Normal file
1
Assets/StreamingAssets/test.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
MyTEST
|
||||||
7
Assets/StreamingAssets/test.txt.meta
Normal file
7
Assets/StreamingAssets/test.txt.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7fc83f79aef22a2449d5e9ca4964b2e0
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/Tests/Editor.meta
Normal file
8
Assets/Tests/Editor.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 649f696fcc0c3104a8de82a2550d248c
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
405
Assets/Tests/Editor/AsyncTestEditor.cs
Normal file
405
Assets/Tests/Editor/AsyncTestEditor.cs
Normal file
@@ -0,0 +1,405 @@
|
|||||||
|
#if !(UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2)
|
||||||
|
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||||
|
|
||||||
|
using UnityEngine;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
using UnityEngine.Scripting;
|
||||||
|
using UniRx;
|
||||||
|
using UniRx.Async;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
#endif
|
||||||
|
using UnityEngine.Networking;
|
||||||
|
|
||||||
|
#if !UNITY_2019_3_OR_NEWER
|
||||||
|
using UnityEngine.Experimental.LowLevel;
|
||||||
|
#else
|
||||||
|
using UnityEngine.LowLevel;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !UNITY_WSA
|
||||||
|
using Unity.Jobs;
|
||||||
|
#endif
|
||||||
|
using Unity.Collections;
|
||||||
|
using System.Threading;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using UnityEngine.TestTools;
|
||||||
|
using FluentAssertions;
|
||||||
|
|
||||||
|
namespace UniRx.AsyncTests
|
||||||
|
{
|
||||||
|
public class AsyncTest
|
||||||
|
{
|
||||||
|
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||||
|
#if !UNITY_WSA
|
||||||
|
|
||||||
|
public struct MyJob : IJob
|
||||||
|
{
|
||||||
|
public int loopCount;
|
||||||
|
public NativeArray<int> inOut;
|
||||||
|
public int result;
|
||||||
|
|
||||||
|
public void Execute()
|
||||||
|
{
|
||||||
|
result = 0;
|
||||||
|
for (int i = 0; i < loopCount; i++)
|
||||||
|
{
|
||||||
|
result++;
|
||||||
|
}
|
||||||
|
inOut[0] = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator DelayAnd() => UniTask.ToCoroutine(async () =>
|
||||||
|
{
|
||||||
|
await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
|
||||||
|
|
||||||
|
var time = Time.realtimeSinceStartup;
|
||||||
|
|
||||||
|
Time.timeScale = 0.5f;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await UniTask.Delay(TimeSpan.FromSeconds(3));
|
||||||
|
|
||||||
|
var elapsed = Time.realtimeSinceStartup - time;
|
||||||
|
((int)Math.Round(TimeSpan.FromSeconds(elapsed).TotalSeconds, MidpointRounding.ToEven)).Should().Be(6);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Time.timeScale = 1.0f;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
[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;
|
||||||
|
((int)Math.Round(TimeSpan.FromSeconds(elapsed).TotalSeconds, MidpointRounding.ToEven)).Should().Be(3);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Time.timeScale = 1.0f;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator WhenAll() => UniTask.ToCoroutine(async () =>
|
||||||
|
{
|
||||||
|
var a = UniTask.FromResult(999);
|
||||||
|
var b = UniTask.Yield(PlayerLoopTiming.Update, CancellationToken.None).AsAsyncUnitUniTask();
|
||||||
|
var c = UniTask.DelayFrame(99);
|
||||||
|
|
||||||
|
var (a2, b2, c2) = await UniTask.WhenAll(a, b, c);
|
||||||
|
a2.Should().Be(999);
|
||||||
|
b2.Should().Be(AsyncUnit.Default);
|
||||||
|
c2.Should().Be(99);
|
||||||
|
});
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator WhenAny() => UniTask.ToCoroutine(async () =>
|
||||||
|
{
|
||||||
|
var a = UniTask.FromResult(999);
|
||||||
|
var b = UniTask.Yield(PlayerLoopTiming.Update, CancellationToken.None).AsAsyncUnitUniTask();
|
||||||
|
var c = UniTask.DelayFrame(99);
|
||||||
|
|
||||||
|
var (win, a2, b2, c2) = await UniTask.WhenAny(a, b, c);
|
||||||
|
win.Should().Be(0);
|
||||||
|
a2.hasResult.Should().Be(true);
|
||||||
|
a2.result0.Should().Be(999);
|
||||||
|
b2.hasResult.Should().Be(false);
|
||||||
|
c2.hasResult.Should().Be(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator BothEnumeratorCheck() => UniTask.ToCoroutine(async () =>
|
||||||
|
{
|
||||||
|
await ToaruCoroutineEnumerator(); // wait 5 frame:)
|
||||||
|
await ToaruCoroutineEnumerator().ConfigureAwait(PlayerLoopTiming.PostLateUpdate);
|
||||||
|
});
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator JobSystem() => UniTask.ToCoroutine(async () =>
|
||||||
|
{
|
||||||
|
var job = new MyJob() { loopCount = 999, inOut = new NativeArray<int>(1, Allocator.TempJob) };
|
||||||
|
JobHandle.ScheduleBatchedJobs();
|
||||||
|
await job.Schedule();
|
||||||
|
job.inOut[0].Should().Be(999);
|
||||||
|
job.inOut.Dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
class MyMyClass
|
||||||
|
{
|
||||||
|
public int MyProperty { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator WaitUntil() => UniTask.ToCoroutine(async () =>
|
||||||
|
{
|
||||||
|
bool t = false;
|
||||||
|
|
||||||
|
await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
|
||||||
|
|
||||||
|
UniTask.DelayFrame(10,PlayerLoopTiming.PostLateUpdate).ContinueWith(_ => t = true).Forget();
|
||||||
|
|
||||||
|
var startFrame = Time.frameCount;
|
||||||
|
await UniTask.WaitUntil(() => t, PlayerLoopTiming.EarlyUpdate);
|
||||||
|
|
||||||
|
var diff = Time.frameCount - startFrame;
|
||||||
|
diff.Should().Be(11);
|
||||||
|
});
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator WaitWhile() => UniTask.ToCoroutine(async () =>
|
||||||
|
{
|
||||||
|
bool t = true;
|
||||||
|
|
||||||
|
UniTask.DelayFrame(10, PlayerLoopTiming.PostLateUpdate).ContinueWith(_ => t = false).Forget();
|
||||||
|
|
||||||
|
var startFrame = Time.frameCount;
|
||||||
|
await UniTask.WaitWhile(() => t, PlayerLoopTiming.EarlyUpdate);
|
||||||
|
|
||||||
|
var diff = Time.frameCount - startFrame;
|
||||||
|
diff.Should().Be(11);
|
||||||
|
});
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator WaitUntilValueChanged() => UniTask.ToCoroutine(async () =>
|
||||||
|
{
|
||||||
|
var v = new MyMyClass { MyProperty = 99 };
|
||||||
|
|
||||||
|
UniTask.DelayFrame(10, PlayerLoopTiming.PostLateUpdate).ContinueWith(_ => v.MyProperty = 1000).Forget();
|
||||||
|
|
||||||
|
var startFrame = Time.frameCount;
|
||||||
|
await UniTask.WaitUntilValueChanged(v, x => x.MyProperty, PlayerLoopTiming.EarlyUpdate);
|
||||||
|
|
||||||
|
var diff = Time.frameCount - startFrame;
|
||||||
|
diff.Should().Be(11);
|
||||||
|
});
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator SwitchTo() => UniTask.ToCoroutine(async () =>
|
||||||
|
{
|
||||||
|
await UniTask.Yield();
|
||||||
|
|
||||||
|
var currentThreadId = Thread.CurrentThread.ManagedThreadId;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
await UniTask.SwitchToThreadPool();
|
||||||
|
//await UniTask.SwitchToThreadPool();
|
||||||
|
//await UniTask.SwitchToThreadPool();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var switchedThreadId = Thread.CurrentThread.ManagedThreadId;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
currentThreadId.Should().NotBe(switchedThreadId);
|
||||||
|
|
||||||
|
|
||||||
|
await UniTask.Yield();
|
||||||
|
|
||||||
|
var switchedThreadId2 = Thread.CurrentThread.ManagedThreadId;
|
||||||
|
|
||||||
|
currentThreadId.Should().Be(switchedThreadId2);
|
||||||
|
});
|
||||||
|
|
||||||
|
//[UnityTest]
|
||||||
|
//public IEnumerator ObservableConversion() => UniTask.ToCoroutine(async () =>
|
||||||
|
//{
|
||||||
|
// var v = await Observable.Range(1, 10).ToUniTask();
|
||||||
|
// v.Is(10);
|
||||||
|
|
||||||
|
// v = await Observable.Range(1, 10).ToUniTask(useFirstValue: true);
|
||||||
|
// v.Is(1);
|
||||||
|
|
||||||
|
// v = await UniTask.DelayFrame(10).ToObservable().ToTask();
|
||||||
|
// v.Is(10);
|
||||||
|
|
||||||
|
// v = await UniTask.FromResult(99).ToObservable();
|
||||||
|
// v.Is(99);
|
||||||
|
//});
|
||||||
|
|
||||||
|
//[UnityTest]
|
||||||
|
//public IEnumerator AwaitableReactiveProperty() => UniTask.ToCoroutine(async () =>
|
||||||
|
//{
|
||||||
|
// var rp1 = new ReactiveProperty<int>(99);
|
||||||
|
|
||||||
|
// UniTask.DelayFrame(100).ContinueWith(x => rp1.Value = x).Forget();
|
||||||
|
|
||||||
|
// await rp1;
|
||||||
|
|
||||||
|
// rp1.Value.Is(100);
|
||||||
|
|
||||||
|
// // var delay2 = UniTask.DelayFrame(10);
|
||||||
|
// // var (a, b ) = await UniTask.WhenAll(rp1.WaitUntilValueChangedAsync(), delay2);
|
||||||
|
|
||||||
|
//});
|
||||||
|
|
||||||
|
//[UnityTest]
|
||||||
|
//public IEnumerator AwaitableReactiveCommand() => UniTask.ToCoroutine(async () =>
|
||||||
|
//{
|
||||||
|
// var rc = new ReactiveCommand<int>();
|
||||||
|
|
||||||
|
// UniTask.DelayFrame(100).ContinueWith(x => rc.Execute(x)).Forget();
|
||||||
|
|
||||||
|
// var v = await rc;
|
||||||
|
|
||||||
|
// v.Is(100);
|
||||||
|
//});
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator ExceptionlessCancellation() => UniTask.ToCoroutine(async () =>
|
||||||
|
{
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
UniTask.DelayFrame(10).ContinueWith(_ => cts.Cancel()).Forget();
|
||||||
|
|
||||||
|
var first = Time.frameCount;
|
||||||
|
var (canceled, value) = await UniTask.DelayFrame(100, cancellationToken: cts.Token).SuppressCancellationThrow();
|
||||||
|
|
||||||
|
(Time.frameCount - first).Should().Be(11); // 10 frame canceled
|
||||||
|
canceled.Should().Be(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator ExceptionCancellation() => UniTask.ToCoroutine(async () =>
|
||||||
|
{
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
UniTask.DelayFrame(10).ContinueWith(_ => cts.Cancel()).Forget();
|
||||||
|
|
||||||
|
bool occur = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var value = await UniTask.DelayFrame(100, cancellationToken: cts.Token);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
occur = true;
|
||||||
|
}
|
||||||
|
occur.Should().BeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
IEnumerator ToaruCoroutineEnumerator()
|
||||||
|
{
|
||||||
|
yield return null;
|
||||||
|
yield return null;
|
||||||
|
yield return null;
|
||||||
|
yield return null;
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator ExceptionUnobserved1() => UniTask.ToCoroutine(async () =>
|
||||||
|
{
|
||||||
|
bool calledEx = false;
|
||||||
|
Action<Exception> action = exx =>
|
||||||
|
{
|
||||||
|
calledEx = true;
|
||||||
|
exx.Message.Should().Be("MyException");
|
||||||
|
};
|
||||||
|
|
||||||
|
UniTaskScheduler.UnobservedTaskException += action;
|
||||||
|
|
||||||
|
var ex = InException1();
|
||||||
|
ex = default(UniTask);
|
||||||
|
|
||||||
|
await UniTask.DelayFrame(3);
|
||||||
|
|
||||||
|
GC.Collect();
|
||||||
|
GC.WaitForPendingFinalizers();
|
||||||
|
GC.Collect();
|
||||||
|
|
||||||
|
await UniTask.DelayFrame(1);
|
||||||
|
|
||||||
|
calledEx.Should().BeTrue();
|
||||||
|
|
||||||
|
UniTaskScheduler.UnobservedTaskException -= action;
|
||||||
|
});
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator ExceptionUnobserved2() => UniTask.ToCoroutine(async () =>
|
||||||
|
{
|
||||||
|
bool calledEx = false;
|
||||||
|
Action<Exception> action = exx =>
|
||||||
|
{
|
||||||
|
calledEx = true;
|
||||||
|
exx.Message.Should().Be("MyException");
|
||||||
|
};
|
||||||
|
|
||||||
|
UniTaskScheduler.UnobservedTaskException += action;
|
||||||
|
|
||||||
|
var ex = InException2();
|
||||||
|
ex = default(UniTask<int>);
|
||||||
|
|
||||||
|
await UniTask.DelayFrame(3);
|
||||||
|
|
||||||
|
GC.Collect();
|
||||||
|
GC.WaitForPendingFinalizers();
|
||||||
|
GC.Collect();
|
||||||
|
|
||||||
|
await UniTask.DelayFrame(1);
|
||||||
|
|
||||||
|
calledEx.Should().BeTrue();
|
||||||
|
|
||||||
|
UniTaskScheduler.UnobservedTaskException -= action;
|
||||||
|
});
|
||||||
|
|
||||||
|
async UniTask InException1()
|
||||||
|
{
|
||||||
|
await UniTask.Yield();
|
||||||
|
throw new Exception("MyException");
|
||||||
|
}
|
||||||
|
|
||||||
|
async UniTask<int> InException2()
|
||||||
|
{
|
||||||
|
await UniTask.Yield();
|
||||||
|
throw new Exception("MyException");
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator NestedEnumerator() => UniTask.ToCoroutine(async () =>
|
||||||
|
{
|
||||||
|
var time = Time.realtimeSinceStartup;
|
||||||
|
|
||||||
|
await ParentCoroutineEnumerator();
|
||||||
|
|
||||||
|
var elapsed = Time.realtimeSinceStartup - time;
|
||||||
|
((int)Math.Round(TimeSpan.FromSeconds(elapsed).TotalSeconds, MidpointRounding.ToEven)).Should().Be(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
IEnumerator ParentCoroutineEnumerator()
|
||||||
|
{
|
||||||
|
yield return ChildCoroutineEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator ChildCoroutineEnumerator()
|
||||||
|
{
|
||||||
|
yield return new WaitForSeconds(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
11
Assets/Tests/Editor/AsyncTestEditor.cs.meta
Normal file
11
Assets/Tests/Editor/AsyncTestEditor.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 851899ba3337ac24a8331cb18fabe771
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
96
Assets/Tests/Editor/RunTestEditor.cs
Normal file
96
Assets/Tests/Editor/RunTestEditor.cs
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#if !(UNITY_4_5 || UNITY_4_6 || UNITY_4_7 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2)
|
||||||
|
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||||
|
|
||||||
|
using UnityEngine;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
using UnityEngine.Scripting;
|
||||||
|
using UniRx;
|
||||||
|
using UniRx.Async;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
#endif
|
||||||
|
using UnityEngine.Networking;
|
||||||
|
|
||||||
|
#if !UNITY_2019_3_OR_NEWER
|
||||||
|
using UnityEngine.Experimental.LowLevel;
|
||||||
|
#else
|
||||||
|
using UnityEngine.LowLevel;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !UNITY_WSA
|
||||||
|
using Unity.Jobs;
|
||||||
|
#endif
|
||||||
|
using Unity.Collections;
|
||||||
|
using System.Threading;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using UnityEngine.TestTools;
|
||||||
|
using FluentAssertions;
|
||||||
|
|
||||||
|
namespace UniRx.AsyncTests
|
||||||
|
{
|
||||||
|
public class RunTest
|
||||||
|
{
|
||||||
|
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||||
|
#if !UNITY_WSA
|
||||||
|
|
||||||
|
//[UnityTest]
|
||||||
|
//public IEnumerator RunThread() => UniTask.ToCoroutine(async () =>
|
||||||
|
//{
|
||||||
|
// var main = Thread.CurrentThread.ManagedThreadId;
|
||||||
|
// var v = await UniTask.Run(() => { return System.Threading.Thread.CurrentThread.ManagedThreadId; }, false);
|
||||||
|
// UnityEngine.Debug.Log("Ret Value is:" + v);
|
||||||
|
// UnityEngine.Debug.Log("Run Here and id:" + System.Threading.Thread.CurrentThread.ManagedThreadId);
|
||||||
|
// //v.Should().Be(3);
|
||||||
|
// main.Should().NotBe(Thread.CurrentThread.ManagedThreadId);
|
||||||
|
//});
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator RunThreadConfigure() => UniTask.ToCoroutine(async () =>
|
||||||
|
{
|
||||||
|
var main = Thread.CurrentThread.ManagedThreadId;
|
||||||
|
var v = await UniTask.Run(() => 3, true);
|
||||||
|
v.Should().Be(3);
|
||||||
|
main.Should().Be(Thread.CurrentThread.ManagedThreadId);
|
||||||
|
});
|
||||||
|
|
||||||
|
//[UnityTest]
|
||||||
|
//public IEnumerator RunThreadException() => UniTask.ToCoroutine(async () =>
|
||||||
|
//{
|
||||||
|
// var main = Thread.CurrentThread.ManagedThreadId;
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// await UniTask.Run<int>(() => throw new Exception(), false);
|
||||||
|
// }
|
||||||
|
// catch
|
||||||
|
// {
|
||||||
|
// main.Should().NotBe(Thread.CurrentThread.ManagedThreadId);
|
||||||
|
// }
|
||||||
|
//});
|
||||||
|
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator RunThreadExceptionConfigure() => UniTask.ToCoroutine(async () =>
|
||||||
|
{
|
||||||
|
var main = Thread.CurrentThread.ManagedThreadId;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await UniTask.Run<int>(() => throw new Exception(), true);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
main.Should().Be(Thread.CurrentThread.ManagedThreadId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
11
Assets/Tests/Editor/RunTestEditor.cs.meta
Normal file
11
Assets/Tests/Editor/RunTestEditor.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 76d078e5d960ac2429ce67a930c29ade
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
23
Assets/Tests/Editor/UniRx.Async.Tests.Editor.asmdef
Normal file
23
Assets/Tests/Editor/UniRx.Async.Tests.Editor.asmdef
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "UniRx.Async.Tests.Editor",
|
||||||
|
"references": [
|
||||||
|
"UnityEngine.TestRunner",
|
||||||
|
"UnityEditor.TestRunner",
|
||||||
|
"UniRx.Async",
|
||||||
|
"UniRx.Async.Tests"
|
||||||
|
],
|
||||||
|
"includePlatforms": [
|
||||||
|
"Editor"
|
||||||
|
],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": true,
|
||||||
|
"precompiledReferences": [
|
||||||
|
"nunit.framework.dll"
|
||||||
|
],
|
||||||
|
"autoReferenced": false,
|
||||||
|
"defineConstraints": [
|
||||||
|
"UNITY_INCLUDE_TESTS"
|
||||||
|
],
|
||||||
|
"versionDefines": []
|
||||||
|
}
|
||||||
7
Assets/Tests/Editor/UniRx.Async.Tests.Editor.asmdef.meta
Normal file
7
Assets/Tests/Editor/UniRx.Async.Tests.Editor.asmdef.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c2431de67d1d97a48afcaf18f333a0b4
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
44
Assets/Tests/Editor/WhenAnyTestEditor.cs
Normal file
44
Assets/Tests/Editor/WhenAnyTestEditor.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||||
|
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||||
|
|
||||||
|
using UnityEngine;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
using UnityEngine.Scripting;
|
||||||
|
using UniRx;
|
||||||
|
using UniRx.Async;
|
||||||
|
using Unity.Collections;
|
||||||
|
using System.Threading;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using UnityEngine.TestTools;
|
||||||
|
using FluentAssertions;
|
||||||
|
|
||||||
|
namespace UniRx.AsyncTests
|
||||||
|
{
|
||||||
|
public class WhenAnyTest
|
||||||
|
{
|
||||||
|
[UnityTest]
|
||||||
|
public IEnumerator WhenAnyCanceled() => UniTask.ToCoroutine(async () =>
|
||||||
|
{
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
var successDelayTask = UniTask.Delay(TimeSpan.FromSeconds(1));
|
||||||
|
var cancelTask = UniTask.Delay(TimeSpan.FromSeconds(1), cancellationToken: cts.Token);
|
||||||
|
cts.CancelAfterSlim(200);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var r = await UniTask.WhenAny(new[] { successDelayTask, cancelTask });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ex.Should().BeAssignableTo<OperationCanceledException>();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
11
Assets/Tests/Editor/WhenAnyTestEditor.cs.meta
Normal file
11
Assets/Tests/Editor/WhenAnyTestEditor.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b5addd9aa1a794441af032a71208b495
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -40,7 +40,7 @@ namespace UniRx.Async
|
|||||||
public static void RegisterRaiseCancelOnDestroy(this CancellationTokenSource cts, GameObject gameObject)
|
public static void RegisterRaiseCancelOnDestroy(this CancellationTokenSource cts, GameObject gameObject)
|
||||||
{
|
{
|
||||||
var trigger = gameObject.GetAsyncDestroyTrigger();
|
var trigger = gameObject.GetAsyncDestroyTrigger();
|
||||||
trigger.AddCancellationTriggerOnDestory(cts);
|
trigger.AddCancellationTriggerOnDestroy(cts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||||
|
|
||||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||||
|
#pragma warning disable CS0436
|
||||||
|
|
||||||
namespace System.Runtime.CompilerServices
|
namespace System.Runtime.CompilerServices
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace UniRx.Async.Editor
|
|||||||
|
|
||||||
static UniTaskTrackerWindow window;
|
static UniTaskTrackerWindow window;
|
||||||
|
|
||||||
[MenuItem("Window/UniRx/UniTask Tracker")]
|
[MenuItem("Window/UniTask Tracker")]
|
||||||
public static void OpenWindow()
|
public static void OpenWindow()
|
||||||
{
|
{
|
||||||
if (window != null)
|
if (window != null)
|
||||||
|
|||||||
@@ -64,6 +64,15 @@ namespace UniRx.Async.Internal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
actionListCount = 0;
|
||||||
|
actionList = new Action[InitialSize];
|
||||||
|
|
||||||
|
waitingListCount = 0;
|
||||||
|
waitingList = new Action[InitialSize];
|
||||||
|
}
|
||||||
|
|
||||||
public void Run()
|
public void Run()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -45,6 +45,17 @@ namespace UniRx.Async.Internal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
lock (arrayLock)
|
||||||
|
{
|
||||||
|
for (var index = 0; index < loopItems.Length; index++)
|
||||||
|
{
|
||||||
|
loopItems[index] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Run()
|
public void Run()
|
||||||
{
|
{
|
||||||
lock (runningAndQueueLock)
|
lock (runningAndQueueLock)
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ using UnityEngine.LowLevel;
|
|||||||
using UnityEngine.Experimental.LowLevel;
|
using UnityEngine.Experimental.LowLevel;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
using UnityEditor;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace UniRx.Async
|
namespace UniRx.Async
|
||||||
{
|
{
|
||||||
public static class UniTaskLoopRunners
|
public static class UniTaskLoopRunners
|
||||||
@@ -62,42 +66,38 @@ namespace UniRx.Async
|
|||||||
static ContinuationQueue[] yielders;
|
static ContinuationQueue[] yielders;
|
||||||
static PlayerLoopRunner[] runners;
|
static PlayerLoopRunner[] runners;
|
||||||
|
|
||||||
static PlayerLoopSystem[] InsertRunner(PlayerLoopSystem loopSystem, Type loopRunnerYieldType, ContinuationQueue cq, Type loopRunnerType, PlayerLoopRunner runner)
|
static PlayerLoopSystem[] InsertRunner(PlayerLoopSystem loopSystem, Type loopRunnerYieldType,
|
||||||
|
ContinuationQueue cq, Type loopRunnerType, PlayerLoopRunner runner)
|
||||||
{
|
{
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
EditorApplication.playModeStateChanged += (state) =>
|
||||||
|
{
|
||||||
|
if (state == PlayModeStateChange.EnteredEditMode ||
|
||||||
|
state == PlayModeStateChange.EnteredPlayMode) return;
|
||||||
|
|
||||||
|
if (runner != null)
|
||||||
|
runner.Clear();
|
||||||
|
if (cq != null)
|
||||||
|
cq.Clear();
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
var yieldLoop = new PlayerLoopSystem
|
var yieldLoop = new PlayerLoopSystem
|
||||||
{
|
{
|
||||||
type = loopRunnerYieldType,
|
type = loopRunnerYieldType,
|
||||||
#if UNITY_EDITOR
|
|
||||||
updateDelegate = () =>
|
|
||||||
{
|
|
||||||
if (Application.isPlaying)
|
|
||||||
{
|
|
||||||
cq.Run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
updateDelegate = cq.Run
|
updateDelegate = cq.Run
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var runnerLoop = new PlayerLoopSystem
|
var runnerLoop = new PlayerLoopSystem
|
||||||
{
|
{
|
||||||
type = loopRunnerType,
|
type = loopRunnerType,
|
||||||
#if UNITY_EDITOR
|
|
||||||
updateDelegate = () =>
|
|
||||||
{
|
|
||||||
if (Application.isPlaying)
|
|
||||||
{
|
|
||||||
runner.Run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
updateDelegate = runner.Run
|
updateDelegate = runner.Run
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var dest = new PlayerLoopSystem[loopSystem.subSystemList.Length + 2];
|
var source = loopSystem.subSystemList // Remove items from previous initializations.
|
||||||
Array.Copy(loopSystem.subSystemList, 0, dest, 2, loopSystem.subSystemList.Length);
|
.Where(ls => ls.type != loopRunnerYieldType && ls.type != loopRunnerType).ToArray();
|
||||||
|
var dest = new PlayerLoopSystem[source.Length + 2];
|
||||||
|
Array.Copy(source, 0, dest, 2, source.Length);
|
||||||
dest[0] = yieldLoop;
|
dest[0] = yieldLoop;
|
||||||
dest[1] = runnerLoop;
|
dest[1] = runnerLoop;
|
||||||
return dest;
|
return dest;
|
||||||
@@ -110,7 +110,15 @@ namespace UniRx.Async
|
|||||||
unitySynchronizationContetext = SynchronizationContext.Current;
|
unitySynchronizationContetext = SynchronizationContext.Current;
|
||||||
mainThreadId = Thread.CurrentThread.ManagedThreadId;
|
mainThreadId = Thread.CurrentThread.ManagedThreadId;
|
||||||
|
|
||||||
|
#if UNITY_EDITOR && UNITY_2019_3_OR_NEWER
|
||||||
|
// When domain reload is disabled, re-initialization is required when entering play mode;
|
||||||
|
// otherwise, pending tasks will leak between play mode sessions.
|
||||||
|
var domainReloadDisabled = UnityEditor.EditorSettings.enterPlayModeOptionsEnabled &&
|
||||||
|
UnityEditor.EditorSettings.enterPlayModeOptions.HasFlag(UnityEditor.EnterPlayModeOptions.DisableDomainReload);
|
||||||
|
if (!domainReloadDisabled && runners != null) return;
|
||||||
|
#else
|
||||||
if (runners != null) return; // already initialized
|
if (runners != null) return; // already initialized
|
||||||
|
#endif
|
||||||
|
|
||||||
var playerLoop =
|
var playerLoop =
|
||||||
#if UNITY_2019_3_OR_NEWER
|
#if UNITY_2019_3_OR_NEWER
|
||||||
@@ -122,6 +130,34 @@ namespace UniRx.Async
|
|||||||
Initialize(ref playerLoop);
|
Initialize(ref playerLoop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
[InitializeOnLoadMethod]
|
||||||
|
static void InitOnEditor()
|
||||||
|
{
|
||||||
|
//Execute the play mode init method
|
||||||
|
Init();
|
||||||
|
|
||||||
|
//register an Editor update delegate, used to forcing playerLoop update
|
||||||
|
EditorApplication.update += ForceEditorPlayerLoopUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static void ForceEditorPlayerLoopUpdate()
|
||||||
|
{
|
||||||
|
if (EditorApplication.isPlayingOrWillChangePlaymode || EditorApplication.isCompiling ||
|
||||||
|
EditorApplication.isUpdating)
|
||||||
|
{
|
||||||
|
// Not in Edit mode, don't interfere
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//force unity to update PlayerLoop callbacks
|
||||||
|
EditorApplication.QueuePlayerLoopUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
public static void Initialize(ref PlayerLoopSystem playerLoop)
|
public static void Initialize(ref PlayerLoopSystem playerLoop)
|
||||||
{
|
{
|
||||||
yielders = new ContinuationQueue[7];
|
yielders = new ContinuationQueue[7];
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ namespace UniRx.Async.Triggers
|
|||||||
bool called = false;
|
bool called = false;
|
||||||
UniTaskCompletionSource promise;
|
UniTaskCompletionSource promise;
|
||||||
CancellationTokenSource cancellationTokenSource; // main cancellation
|
CancellationTokenSource cancellationTokenSource; // main cancellation
|
||||||
object canellationTokenSourceOrQueue; // external from AddCancellationTriggerOnDestory
|
object canellationTokenSourceOrQueue; // external from AddCancellationTriggerOnDestroy
|
||||||
|
|
||||||
public CancellationToken CancellationToken
|
public CancellationToken CancellationToken
|
||||||
{
|
{
|
||||||
@@ -63,7 +63,7 @@ namespace UniRx.Async.Triggers
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Add Cancellation Triggers on destroy</summary>
|
/// <summary>Add Cancellation Triggers on destroy</summary>
|
||||||
public void AddCancellationTriggerOnDestory(CancellationTokenSource cts)
|
public void AddCancellationTriggerOnDestroy(CancellationTokenSource cts)
|
||||||
{
|
{
|
||||||
if (called)
|
if (called)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,6 +3,25 @@
|
|||||||
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using UniRx.Async.Triggers;
|
||||||
|
|
||||||
|
namespace UniRx.Async
|
||||||
|
{
|
||||||
|
public static class UniTaskCancellationExtensions
|
||||||
|
{
|
||||||
|
/// <summary>This CancellationToken is canceled when the MonoBehaviour will be destroyed.</summary>
|
||||||
|
public static CancellationToken GetCancellationTokenOnDestroy(this GameObject gameObject)
|
||||||
|
{
|
||||||
|
return gameObject.GetAsyncDestroyTrigger().CancellationToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>This CancellationToken is canceled when the MonoBehaviour will be destroyed.</summary>
|
||||||
|
public static CancellationToken GetCancellationTokenOnDestroy(this Component component)
|
||||||
|
{
|
||||||
|
return component.GetAsyncDestroyTrigger().CancellationToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace UniRx.Async.Triggers
|
namespace UniRx.Async.Triggers
|
||||||
{
|
{
|
||||||
@@ -36,18 +55,6 @@ namespace UniRx.Async.Triggers
|
|||||||
return component.GetAsyncDestroyTrigger().OnDestroyAsync();
|
return component.GetAsyncDestroyTrigger().OnDestroyAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>This CancellationToken is canceled when the MonoBehaviour will be destroyed.</summary>
|
|
||||||
public static CancellationToken GetCancellationTokenOnDestroy(this GameObject gameObject)
|
|
||||||
{
|
|
||||||
return gameObject.GetAsyncDestroyTrigger().CancellationToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>This CancellationToken is canceled when the MonoBehaviour will be destroyed.</summary>
|
|
||||||
public static CancellationToken GetCancellationTokenOnDestroy(this Component component)
|
|
||||||
{
|
|
||||||
return component.GetAsyncDestroyTrigger().CancellationToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask StartAsync(this GameObject gameObject)
|
public static UniTask StartAsync(this GameObject gameObject)
|
||||||
{
|
{
|
||||||
return gameObject.GetAsyncStartTrigger().StartAsync();
|
return gameObject.GetAsyncStartTrigger().StartAsync();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||||
|
#pragma warning disable CS0436
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable CS0436
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|||||||
@@ -83,6 +83,30 @@ namespace UniRx.Async
|
|||||||
return new UniTask<UnityEngine.Object>(awaiter);
|
return new UniTask<UnityEngine.Object>(awaiter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static AssetBundleCreateRequestAwaiter GetAwaiter(this AssetBundleCreateRequest resourceRequest)
|
||||||
|
{
|
||||||
|
Error.ThrowArgumentNullException(resourceRequest, nameof(resourceRequest));
|
||||||
|
return new AssetBundleCreateRequestAwaiter(resourceRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UniTask<AssetBundle> ToUniTask(this AssetBundleCreateRequest resourceRequest)
|
||||||
|
{
|
||||||
|
Error.ThrowArgumentNullException(resourceRequest, nameof(resourceRequest));
|
||||||
|
return new UniTask<AssetBundle>(new AssetBundleCreateRequestAwaiter(resourceRequest));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UniTask<AssetBundle> ConfigureAwait(this AssetBundleCreateRequest resourceRequest, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken))
|
||||||
|
{
|
||||||
|
Error.ThrowArgumentNullException(resourceRequest, nameof(resourceRequest));
|
||||||
|
|
||||||
|
var awaiter = new AssetBundleCreateRequestConfiguredAwaiter(resourceRequest, progress, cancellation);
|
||||||
|
if (!awaiter.IsCompleted)
|
||||||
|
{
|
||||||
|
PlayerLoopHelper.AddAction(timing, awaiter);
|
||||||
|
}
|
||||||
|
return new UniTask<AssetBundle>(awaiter);
|
||||||
|
}
|
||||||
|
|
||||||
#if ENABLE_WWW
|
#if ENABLE_WWW
|
||||||
|
|
||||||
#if UNITY_2018_3_OR_NEWER
|
#if UNITY_2018_3_OR_NEWER
|
||||||
@@ -627,6 +651,165 @@ namespace UniRx.Async
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct AssetBundleCreateRequestAwaiter : IAwaiter<AssetBundle>
|
||||||
|
{
|
||||||
|
AssetBundleCreateRequest asyncOperation;
|
||||||
|
Action<AsyncOperation> continuationAction;
|
||||||
|
AwaiterStatus status;
|
||||||
|
AssetBundle result;
|
||||||
|
|
||||||
|
public AssetBundleCreateRequestAwaiter(AssetBundleCreateRequest asyncOperation)
|
||||||
|
{
|
||||||
|
this.status = asyncOperation.isDone ? AwaiterStatus.Succeeded : AwaiterStatus.Pending;
|
||||||
|
this.asyncOperation = (this.status.IsCompleted()) ? null : asyncOperation;
|
||||||
|
this.result = (this.status.IsCompletedSuccessfully()) ? asyncOperation.assetBundle : null;
|
||||||
|
this.continuationAction = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsCompleted => status.IsCompleted();
|
||||||
|
public AwaiterStatus Status => status;
|
||||||
|
|
||||||
|
public AssetBundle GetResult()
|
||||||
|
{
|
||||||
|
if (status == AwaiterStatus.Succeeded) return this.result;
|
||||||
|
|
||||||
|
if (status == AwaiterStatus.Pending)
|
||||||
|
{
|
||||||
|
// first timing of call
|
||||||
|
if (asyncOperation.isDone)
|
||||||
|
{
|
||||||
|
status = AwaiterStatus.Succeeded;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Error.ThrowNotYetCompleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.result = asyncOperation.assetBundle;
|
||||||
|
|
||||||
|
if (continuationAction != null)
|
||||||
|
{
|
||||||
|
asyncOperation.completed -= continuationAction;
|
||||||
|
asyncOperation = null; // remove reference.
|
||||||
|
continuationAction = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
asyncOperation = null; // remove reference.
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IAwaiter.GetResult() => GetResult();
|
||||||
|
|
||||||
|
public void OnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
UnsafeOnCompleted(continuation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnsafeOnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
|
||||||
|
continuationAction = continuation.AsFuncOfT<AsyncOperation>();
|
||||||
|
asyncOperation.completed += continuationAction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AssetBundleCreateRequestConfiguredAwaiter : IAwaiter<AssetBundle>, IPlayerLoopItem
|
||||||
|
{
|
||||||
|
AssetBundleCreateRequest asyncOperation;
|
||||||
|
IProgress<float> progress;
|
||||||
|
CancellationToken cancellationToken;
|
||||||
|
AwaiterStatus status;
|
||||||
|
Action continuation;
|
||||||
|
AssetBundle result;
|
||||||
|
|
||||||
|
public AssetBundleCreateRequestConfiguredAwaiter(AssetBundleCreateRequest asyncOperation, IProgress<float> progress, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
this.status = cancellationToken.IsCancellationRequested ? AwaiterStatus.Canceled
|
||||||
|
: asyncOperation.isDone ? AwaiterStatus.Succeeded
|
||||||
|
: AwaiterStatus.Pending;
|
||||||
|
|
||||||
|
if (this.status.IsCompletedSuccessfully()) this.result = asyncOperation.assetBundle;
|
||||||
|
if (this.status.IsCompleted()) return;
|
||||||
|
|
||||||
|
this.asyncOperation = asyncOperation;
|
||||||
|
this.progress = progress;
|
||||||
|
this.cancellationToken = cancellationToken;
|
||||||
|
this.continuation = null;
|
||||||
|
this.result = null;
|
||||||
|
|
||||||
|
TaskTracker.TrackActiveTask(this, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsCompleted => status.IsCompleted();
|
||||||
|
public AwaiterStatus Status => status;
|
||||||
|
void IAwaiter.GetResult() => GetResult();
|
||||||
|
|
||||||
|
public AssetBundle GetResult()
|
||||||
|
{
|
||||||
|
if (status == AwaiterStatus.Succeeded) return this.result;
|
||||||
|
|
||||||
|
if (status == AwaiterStatus.Canceled)
|
||||||
|
{
|
||||||
|
Error.ThrowOperationCanceledException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Error.ThrowNotYetCompleted<AssetBundle>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
InvokeContinuation(AwaiterStatus.Canceled);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progress != null)
|
||||||
|
{
|
||||||
|
progress.Report(asyncOperation.progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (asyncOperation.isDone)
|
||||||
|
{
|
||||||
|
this.result = asyncOperation.assetBundle;
|
||||||
|
InvokeContinuation(AwaiterStatus.Succeeded);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InvokeContinuation(AwaiterStatus status)
|
||||||
|
{
|
||||||
|
this.status = status;
|
||||||
|
var cont = this.continuation;
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
TaskTracker.RemoveTracking(this);
|
||||||
|
this.continuation = null;
|
||||||
|
this.cancellationToken = CancellationToken.None;
|
||||||
|
this.progress = null;
|
||||||
|
this.asyncOperation = null;
|
||||||
|
|
||||||
|
if (cont != null) cont.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
Error.ThrowWhenContinuationIsAlreadyRegistered(this.continuation);
|
||||||
|
this.continuation = continuation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnsafeOnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
Error.ThrowWhenContinuationIsAlreadyRegistered(this.continuation);
|
||||||
|
this.continuation = continuation;
|
||||||
|
}
|
||||||
|
}
|
||||||
#if ENABLE_WWW
|
#if ENABLE_WWW
|
||||||
|
|
||||||
#if UNITY_2018_3_OR_NEWER
|
#if UNITY_2018_3_OR_NEWER
|
||||||
|
|||||||
11
Assets/UniRx.Async/package.json
Normal file
11
Assets/UniRx.Async/package.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "com.cysharp.unitask",
|
||||||
|
"displayName": "UniTask",
|
||||||
|
"version": "1.3.0",
|
||||||
|
"unity": "2018.3",
|
||||||
|
"description": "Provides an efficient async/await integration to Unity.",
|
||||||
|
"keywords": ["async/await", "async", "Task", "UniTask"],
|
||||||
|
"license": "MIT",
|
||||||
|
"category": "Task",
|
||||||
|
"dependencies": {}
|
||||||
|
}
|
||||||
7
Assets/UniRx.Async/package.json.meta
Normal file
7
Assets/UniRx.Async/package.json.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d1a9a71f68bb0d04db91ddaa3329abf9
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -8,4 +8,7 @@ EditorBuildSettings:
|
|||||||
- enabled: 1
|
- enabled: 1
|
||||||
path: Assets/Scenes/SandboxMain.unity
|
path: Assets/Scenes/SandboxMain.unity
|
||||||
guid: 2cda990e2423bbf4892e6590ba056729
|
guid: 2cda990e2423bbf4892e6590ba056729
|
||||||
|
- enabled: 1
|
||||||
|
path: Assets/Scenes/NextScene.unity
|
||||||
|
guid: dfced9d8875377c44a1d53620d36f0d5
|
||||||
m_configObjects: {}
|
m_configObjects: {}
|
||||||
|
|||||||
@@ -3,19 +3,33 @@
|
|||||||
--- !u!159 &1
|
--- !u!159 &1
|
||||||
EditorSettings:
|
EditorSettings:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
serializedVersion: 7
|
serializedVersion: 9
|
||||||
m_ExternalVersionControlSupport: Visible Meta Files
|
m_ExternalVersionControlSupport: Visible Meta Files
|
||||||
m_SerializationMode: 2
|
m_SerializationMode: 2
|
||||||
m_LineEndingsForNewScripts: 2
|
m_LineEndingsForNewScripts: 2
|
||||||
m_DefaultBehaviorMode: 1
|
m_DefaultBehaviorMode: 1
|
||||||
|
m_PrefabRegularEnvironment: {fileID: 0}
|
||||||
|
m_PrefabUIEnvironment: {fileID: 0}
|
||||||
m_SpritePackerMode: 4
|
m_SpritePackerMode: 4
|
||||||
m_SpritePackerPaddingPower: 1
|
m_SpritePackerPaddingPower: 1
|
||||||
m_EtcTextureCompressorBehavior: 1
|
m_EtcTextureCompressorBehavior: 1
|
||||||
m_EtcTextureFastCompressor: 1
|
m_EtcTextureFastCompressor: 1
|
||||||
m_EtcTextureNormalCompressor: 2
|
m_EtcTextureNormalCompressor: 2
|
||||||
m_EtcTextureBestCompressor: 4
|
m_EtcTextureBestCompressor: 4
|
||||||
m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd
|
m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmref
|
||||||
m_ProjectGenerationRootNamespace:
|
m_ProjectGenerationRootNamespace:
|
||||||
m_UserGeneratedProjectSuffix:
|
|
||||||
m_CollabEditorSettings:
|
m_CollabEditorSettings:
|
||||||
inProgressEnabled: 1
|
inProgressEnabled: 1
|
||||||
|
m_EnableTextureStreamingInEditMode: 1
|
||||||
|
m_EnableTextureStreamingInPlayMode: 1
|
||||||
|
m_AsyncShaderCompilation: 1
|
||||||
|
m_EnterPlayModeOptionsEnabled: 0
|
||||||
|
m_EnterPlayModeOptions: 3
|
||||||
|
m_ShowLightmapResolutionOverlay: 1
|
||||||
|
m_UseLegacyProbeSampleCount: 1
|
||||||
|
m_AssetPipelineMode: 1
|
||||||
|
m_CacheServerMode: 0
|
||||||
|
m_CacheServerEndpoint:
|
||||||
|
m_CacheServerNamespacePrefix: default
|
||||||
|
m_CacheServerEnableDownload: 1
|
||||||
|
m_CacheServerEnableUpload: 1
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
m_EditorVersion: 2019.3.0a2
|
m_EditorVersion: 2019.3.9f1
|
||||||
m_EditorVersionWithRevision: 2019.3.0a2 (fa7740529556)
|
m_EditorVersionWithRevision: 2019.3.9f1 (e6e740a1c473)
|
||||||
|
|||||||
181
README.md
181
README.md
@@ -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,142 @@ 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);
|
||||||
|
```
|
||||||
|
|
||||||
|
CancellationToken can create by `CancellationTokenSource` or MonoBehaviour's extension method `GetCancellationTokenOnDestroy`.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// this CancellationToken lifecycle is same as GameObject.
|
||||||
|
await UniTask.DelayFrame(1000, cancellationToken: this.GetCancellationTokenOnDestroy());
|
||||||
|
```
|
||||||
|
|
||||||
|
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`.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
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 +294,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 +342,16 @@ UniTask.DelayFrame
|
|||||||
UniTask.Delay(..., bool ignoreTimeScale = false, ...) parameter
|
UniTask.Delay(..., bool ignoreTimeScale = false, ...) parameter
|
||||||
```
|
```
|
||||||
|
|
||||||
Cancellation
|
UPM Package
|
||||||
---
|
---
|
||||||
|
After Unity 2019.3.4f1, Unity 2020.1a21, that support path query parameter of git package. You can add `https://github.com/Cysharp/UniTask.git?path=Assets/UniRx.Async` to Package Manager
|
||||||
|
|
||||||
async void vs async UniTask/UniTaskVoid
|

|
||||||
---
|
|
||||||
|
|
||||||
Progress
|

|
||||||
---
|
|
||||||
|
|
||||||
For Unit Testing
|
or add `"com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=Assets/UniRx.Async"` to `Packages/manifest.json`.
|
||||||
---
|
|
||||||
|
|
||||||
Reference
|
|
||||||
---
|
|
||||||
|
|
||||||
License
|
License
|
||||||
---
|
---
|
||||||
This library is under the MIT License.
|
This library is under the MIT License.
|
||||||
Reference in New Issue
Block a user