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; }
}
}