Unity的Coroutine非常好用。在很多需要延遲執行的地方,都可以省掉大量功夫。
Coroutine的缺點
普通協程有以下這些缺點:
- 嵌套協程依賴StartCoroutine,從而代碼依賴MonoBehaviour
- 執行異步邏輯的時候,需要寫while等待。代碼臃腫難看
- 協程運行結果無法直接返回,需要另外處理,特別費力
這些缺點導致了下邊這種寫起來很難受的代碼
public IEnumerator Coroutine()
{
//-----------------------------嵌套協程-----------------------------//
yield return StartCoroutine(InternalCoroutine());
//-----------------------------協程等待異步邏輯執行完畢-----------------------------//
var running = false;
DoSomething(() =>
{
running = true;
});
while (running == false)
{
yield return 0;
}
//-----------------------------協程返回值-----------------------------//
running = false;
var rtn = "bbb";
DoSomthing2(s =>
{
running = true;
rtn = s;
});
while (running == false)
{
yield return 0;
}
}
CustomYieldInstruction
後來我看到Unity的一些類繼承了CustomYieldInstruction這個接口,很好的解決了上邊的問題。
public class WWW : CustomYieldInstruction, IDisposable {...}
public class WaitForSecondsRealtime : CustomYieldInstruction {...}
public sealed class WaitUntil : CustomYieldInstruction {...}
public sealed class WaitWhile : CustomYieldInstruction {...}
public abstract class CustomYieldInstruction : IEnumerator
{
/// <summary>
/// <para>Indicates if coroutine should be kept suspended.</para>
/// </summary>
public abstract bool keepWaiting { get; }
public object Current
{
get
{
return (object) null;
}
}
public bool MoveNext()
{
return this.keepWaiting;
}
public void Reset()
{
}
}
比如下邊這個下載文件的協程邏輯,我們把返回值Code放在類中。然後當下載完成後。再設置Code的值使協程被打斷。
var download = new DownloadCoroutine(...);
yield return download;
Debug.Log(download.Code);
public class DownloadCoroutine : CustomYieldInstruction
{
public int Code = -1;
public DownloadCoroutine(string fromPath, string toPath, OnDownloadProgressDelegate onProgress)
{
Debug.Log("下載文件 : " + fromPath + " -> " + toPath);
var httpRequest = new HTTPRequest(new Uri(fromPath), HTTPMethods.Get, (request, response) =>
{
if (response == null)
{
Code = 500;
return;
}
Debug.Log("下載完成 : " + response.StatusCode);
File.WriteAllBytes(toPath, response.Data);
Code = response.StatusCode;
});
httpRequest.OnProgress = onProgress;
httpRequest.Send();
}
public override bool keepWaiting
{
get { return Code == -1; }
}
}