Unitask API
UniTask.WaitUntil
UniTask.WaitWhile
UniTask.WaitUntilValueChanged
UniTask.SwitchToThreadPool
UniTask.SwitchToTaskPool
UniTask.SwitchToMainThread
UniTask.SwitchToSynchronizationContext
UniTask.Yield
UniTask.Run
UniTask.Lazy
UniTask.Void
UniTask.ConfigureAwait
UniTask.DelayFrame
UniTask.Delay(…, bool ignoreTimeScale = false, …) parameter
// 等待100帧
await UniTask.DelayFrame(100);
// 等待10s
await UniTask.Delay(TimeSpan.FromSeconds(10));
// 等到最后一帧
await UniTask.WaitForEndOfFrame();
// 等待固定跟新
await UniTask.WaitForFixedUpdate();
// 等到isActive变为true时通过
await UniTask.WaitUntil(() => isActive == true);
// 等到x.isActive值变化时通过
await UniTask.WaitUntilValueChanged(this, x => x.isActive);
// 可以等待协程
//await FooCoroutineEnumerator();
// 等待标准任务
await UniTask.Run(() => 100);
// 多线程,在此代码下运行在 ThreadPool 上
await UniTask.SwitchToThreadPool();
/* work on ThreadPool */
// 返回主线程(与 UniRx 中的"ObserveOnMainThread"相同)
await UniTask.SwitchToMainThread();
// 获取异步网络请求 using UnityEngine.Networking;
async UniTask<string> GetTextAsync(UnityWebRequest req)
{
var op = await req.SendWebRequest();
return op.downloadHandler.text;
}
var task1 = GetTextAsync(UnityWebRequest.Get("http://google.com"));
var task2 = GetTextAsync(UnityWebRequest.Get("http://bing.com"));
var task3 = GetTextAsync(UnityWebRequest.Get("http://yahoo.com"));
// 并发异步等待并通过元组语法轻松获取结果
var (google, bing, yahoo) = await UniTask.WhenAll(task1, task2, task3);
// WhenAll的简写,tuple可以直接await
var (google2, bing2, yahoo2) = await (task1, task2, task3);
什么是UniTask
UniTask是基于C# 5的async和await异步定制的一套Unity优化的异步解决方案;需要Unity2017以上的版本,并且需要升级c#库, 但并不如UniRx项目提供的更为轻量级,可是UniTask拥有强大的async/await和unity进行交互。
一般什么情况下使用UniTask
一般来说首先想到的是网络相关的处理和资源加载需要用UniTask来处理,使用UniRx来处理感觉不够用;还有复杂的运算也可以使用UniTask,当作一个线程来使用就好。
基于Unity AsyncOperation拓展的async&awaitAPI
所有Unity中的异步AsyncOperation都可以使用await来等待异步结果,包括AsyncLoadScene、AsyncLoadAsset、AsyncOperation
public class No22_UniTask : MonoBehaviour
{
async void StartAsync()
{
// 加载资源
var load = await Resources.LoadAsync<TextAsset>("1");
// 加载场景
await SceneManager.LoadSceneAsync("SampleScene")
.ConfigureAwait(Progress.Create<float>(p => Debug.LogFormat("p:{0}", p)));
// 网络请求
UnityWebRequest req = UnityWebRequest.Get("[http://www.baidu.com](http://www.baidu.com)");
var op = await req.SendWebRequest();
}
void Start()
{
StartAsync();
}
}
Start外部定义方法
public async UniTaskVoid LoadManyAsync()
{
//UnitaskVoid可以写成Unitask
}
Start内部定义方法
Func<UniTaskVoid> DemoAsync = async () =>
{
};
DemoAsync();//只能在Start中执行方法
点击立即事件
//相同按钮的多次点击事件
public Button button;
void Start()
{
Func<UniTaskVoid> DemoAsync = async () =>
{
//await Btn1.GetAsyncClickEventHandler().OnClickAsync();
//await Btn1.OnClickAsync();
//Debug.Log("点击事件");
await TripleClick();
Debug.Log("TripleClick被点击");
};
DemoAsync();
}
async UniTask TripleClick()
{
using (var handler = button.GetAsyncClickEventHandler())
{
await handler.OnClickAsync();
await handler.OnClickAsync();
await handler.OnClickAsync();
Debug.Log("Three times clicked");
}
}
//相同按钮的多次点击事件--使用异步 LINQ 1
public Button button;
void Start()
{
Func<UniTaskVoid> DemoAsync = async () =>
{
await TripleClick();
Debug.Log("TripleClick被点击");
};
DemoAsync();
}
async UniTask TripleClick()
{
await button.OnClickAsAsyncEnumerable().Take(3).LastAsync() ;
Debug.Log("Three times clicked");
}
//相同按钮的多次点击事件--使用异步 LINQ 2
public Button button;
void Start()
{
Func<UniTaskVoid> DemoAsync = async () =>
{
await TripleClick();
Debug.Log("TripleClick被点击");
};
DemoAsync();
}
async UniTask TripleClick()
{
await button.OnClickAsAsyncEnumerable().Take(3).ForEachAsync(_ =>
{
Debug.Log("Every clicked");//每次点击执行事件
});
Debug.Log("Three times clicked, complete.");//点击3次结束后执行事件
}
//不同按钮的顺序点击事件 不受顺序限制可以使用WhenAll代替
public Button button1;
public Button button2;
public Button button3;
void Start()
{
Func<UniTaskVoid> DemoAsync = async () =>
{
await TripleClick();
Debug.Log("TripleClick被点击");
};
DemoAsync();
}
async UniTask TripleClick()
{
await button1.OnClickAsync();
await button2.OnClickAsync();
await button3.OnClickAsync();
Debug.Log("Three times clicked");
}
点击延时响应事件
private bool mValue;
public Button button1;
public Button button2;
public Button button3;
void Start()
{
Func<UniTaskVoid> DemoAsync = async () =>
{
await TripleClick();
Debug.Log("TripleClick被点击");
await button2.OnClickAsync();
Debug.Log(2);
};
DemoAsync();
}
async UniTask TripleClick()
{
//pull-type异步流在序列中的异步处理完成之前不会获得下一个值。这可能会泄漏push-type事件(如按钮)中的数据。
//await button1.OnClickAsAsyncEnumerable().ForEachAwaitAsync(async x =>
//{
// await UniTask.Delay(TimeSpan.FromSeconds(3));
// Debug.Log(1);
// //遍历每次点击的事件隔3s执行一次,不会跳出从等待,因此只会输出 1
//});
//使用Queue()方法,该方法还将在异步处理期间对事件进行排队。
//await button1.OnClickAsAsyncEnumerable().Queue().ForEachAwaitAsync(async x =>
//{
// await UniTask.Delay(TimeSpan.FromSeconds(3));
// Debug.Log(1);
// //遍历每次点击的事件隔3s执行一次,不会跳出从等待,因此只会输出 1
//});
//使用Subscribe,fire-and-forget样式。
button1.OnClickAsAsyncEnumerable().Subscribe(async x =>
{
await UniTask.Delay(TimeSpan.FromSeconds(3));
Debug.Log(1);
//会先输出 TripleClick被点击,然后如果button2被点击一次,会输出 2
//如果button1被点击,则输出1
});
}
UniTask.WhenAll
public Button Btn1;
public Button Btn2;
public Button Btn3;
void Start()
{
DemoAsync();
}
async UniTaskVoid DemoAsync()
{
await UniTask.WhenAll(
GetValue1(),
GetValue2(),
GetValue3());
Debug.Log("全部被点击完");
}
async UniTask GetValue1()
{
await Btn1.OnClickAsync();
Debug.Log(1);
}
async UniTask GetValue2()
{
await Btn2.OnClickAsync();
Debug.Log(2);
}
async UniTask GetValue3()
{
await Btn3.OnClickAsync();
Debug.Log(3);
}
async UniTaskVoid DemoAsync12()
{
await UniTask.WhenAll(
GetValue1(),
GetValue2()
);
}
Unitask.WhenAny
public Button Btn1;
public Button Btn2;
public Button Btn3;
void Start()
{
DemoAsync();
}
async UniTaskVoid DemoAsync()
{
await UniTask.WhenAny(
GetValue1(),
GetValue2(),
GetValue3());
Debug.Log("任何一个被点击");
}
async UniTask GetValue1()
{
await Btn1.OnClickAsync();
Debug.Log(1);
}
async UniTask GetValue2()
{
await Btn2.OnClickAsync();
Debug.Log(2);
}
async UniTask GetValue3()
{
await Btn3.OnClickAsync();
Debug.Log(3);
}
Func<UniTaskVoid> ClickAfterDuanLu = async () =>
{
await UniTask.WhenAny(
GetClick1(),
GetClick2(),
GetClick3());
};
UniTask.WaitUntil 相当于Update每帧检测条件
//一直等待直到达到某种条件,才执行下面的代码
private bool mValue;
public Button Btn;
void Start()
{
//方法1
Func<UniTaskVoid> DemoAsync = async () =>
{
Debug.LogFormat("WaitUntil还未被调用");
await Btn.OnClickAsync();
mValue = true;
await UniTask.WaitUntil(() => mValue == true);
Debug.LogFormat("WaitUntil被调用");
};
//方法2
Func<UniTaskVoid> DemoAsync = async () =>
{
Debug.LogFormat("WaitUntil还未被调用");
await UniTask.WaitUntil(() => mValue == true);
Debug.LogFormat("WaitUntil被调用");
};
Btn.onClick.AddListener(() => { mValue = true; });
DemoAsync();
}
await UniTask.WaitUntil(() => isAll.All(x => x));//等待全部条件变成true
UniTask.WaitWhile
//当达到某种条件才进行等待,条件失败通过
//mValue为true时会执行,即由false变为true时会执行
private bool mValue;
void Start()
{
Func<UniTaskVoid> DemoAsync = async () =>
{
Debug.LogFormat("WaitWhile还未被调用");
await UniTask.Delay(TimeSpan.FromSeconds(5));
mValue = true;
await UniTask.WaitWhile(() => mValue == false);//条件不等于false时通过执行
Debug.LogFormat("WaitWhile被调用");
};
DemoAsync();
}
UniTask.WaitUntilValueChanged
一直等待直到值有变化了之后才之执行后面的代码
UniTask.SwitchToThreadPool
//之后的代码运行在线程池中
public class No22_UniTask : MonoBehaviour
{
private bool mValue;
async void StartAsync()
{
Debug.LogFormat("frame start:{0}", Time.frameCount);
UniTask.DelayFrame(100).ToObservable().Subscribe(_=>mValue=true);
//之后的代码运行中线程池中
await UniTask.SwitchToThreadPool();
// 当达到某种条件才进行等待,条件失败通过
await UniTask.WaitWhile(() => mValue == false);
Debug.LogFormat("frame end:{0}", Time.frameCount);
}
void Start()
{
StartAsync();
}
}
UniTask.SwitchToTaskPool
之后的代码运行在任务池中
UniTask.SwitchToMainThread
*之后的代码运行在主线程中
UniTask.SwitchToSynchronizationContext
之后的代码运行在同步句柄中
UniTask.Yield
相当于yiled return [operation]
public class No22_UniTask : MonoBehaviour
{
async void StartAsync()
{
Debug.LogFormat("frame start:{0}", Time.frameCount);
// PlayerLoopTiming.PostLateUpdate = WaitForEndOfFrame
// yield return null
// yield return WaitForFixedUpdate
await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
Debug.LogFormat("frame end:{0}", Time.frameCount);
}
void Start()
{
StartAsync();
}
}
UniTask.Run
启动一个标准的线程
public class No22_UniTask : MonoBehaviour
{
async void StartAsync()
{
Debug.LogFormat("frame start:{0}", Time.frameCount);
// 启动一个标准的线程
await UniTask.Run(() => Debug.Log("heavy task heavy cal"));
Debug.LogFormat("frame end:{0}", Time.frameCount);
}
void Start()
{
StartAsync();
}
}
UniTask.Lazy
启动一个回调线程
public class No22_UniTask : MonoBehaviour
{
private UniTask<int> mLazy;
async void StartAsync()
{
Debug.LogFormat("frame start:{0}", Time.frameCount);
// 启动一个回调线程
await UniTask.Lazy<int>(() => UniTask.DelayFrame(100));
Debug.LogFormat("frame end:{0}", Time.frameCount);
}
void Start()
{
StartAsync();
}
}
UniTask.Void
void回调辅助方法
public class No22_UniTask : MonoBehaviour
{
async void StartAsync()
{
Debug.LogFormat("frame start:{0}", Time.frameCount);
// void回调辅助方法
UniTask.Void(Method);
Debug.LogFormat("frame end:{0}", Time.frameCount);
}
Func<UniTaskVoid> Method = async () =>
{
await UniTask.DelayFrame(100);
};
void Start()
{
StartAsync();
}
}
UniTask.ConfigureAwait
基于AsyncOperation的拓展配置
public class No22_UniTask : MonoBehaviour
{
async void StartAsync()
{
Debug.LogFormat("frame start:{0}", Time.frameCount);
// 基于AsyncOperation的拓展配置
Resources.LoadAsync<TextAsset>("1").ConfigureAwait(Progress.Create<float>(p => Debug.LogFormat("p:{0}", p)));//此句有问题
Debug.LogFormat("frame end:{0}", Time.frameCount);
}
private UniTask Method()
{
return UniTask.DelayFrame(100);
}
void Start()
{
StartAsync();
}
}
UniTask.DelayFrame
延时指定的帧数执行下面的代码
UniTask.Delay(…, bool ignoreTimeScale = false, …) parameter
延时指定的时间执行下面的代码
UniTaskTracker 可视化任务
使用UniTask的监视器,可以监视当前执行的哪些任务
其他设置
Enable AutoReload:是否允许自动加载task
Enable Tracking: 是否允许跟踪task
Enable StackTrace:是否允许堆栈跟踪
Reload:重新加载task列表
GC.Collect:进行GC回收