-
-
Save krzys-h/9062552e33dd7bd7fe4a6c12db109a1a to your computer and use it in GitHub Desktop.
public class UnityWebRequestAwaiter : INotifyCompletion | |
{ | |
private UnityWebRequestAsyncOperation asyncOp; | |
private Action continuation; | |
public UnityWebRequestAwaiter(UnityWebRequestAsyncOperation asyncOp) | |
{ | |
this.asyncOp = asyncOp; | |
asyncOp.completed += OnRequestCompleted; | |
} | |
public bool IsCompleted { get { return asyncOp.isDone; } } | |
public void GetResult() { } | |
public void OnCompleted(Action continuation) | |
{ | |
this.continuation = continuation; | |
} | |
private void OnRequestCompleted(AsyncOperation obj) | |
{ | |
continuation(); | |
} | |
} | |
public static class ExtensionMethods | |
{ | |
public static UnityWebRequestAwaiter GetAwaiter(this UnityWebRequestAsyncOperation asyncOp) | |
{ | |
return new UnityWebRequestAwaiter(asyncOp); | |
} | |
} | |
/* | |
// Usage example: | |
UnityWebRequest www = new UnityWebRequest(); | |
// ... | |
await www.SendWebRequest(); | |
Debug.Log(req.downloadHandler.text); | |
*/ |
To reduce allocations i suggest to make WebRequestAwaiter as struct and asyncOp.completed += continuation subscribtion only in OnCompleted call. If IsCompleted = true, when asyc state machine will not call OnCompleted but just get the result.
Order of operators in OnCompleted is important, because struct subscribtion make copy of struct in delegate field.
A couple errors there
- struct can't compile if
continuation
isn't assigned, asyncOp.completed
can't be set tocontinuation
, parameter mismatch. It should be set toOnRequestCompleted
(unless changingAction continuation
toAction<AsyncOperation> continuation
, but that wicked limits the callbacks you'd be able to apply,- if
continuation
is null, it's possible it could be called while null, soOnRequestComplete
should accommodate a nullcheck
Updated code (including library imports):
using System;
using UnityEngine.Networking;
using System.Runtime.CompilerServices;
using UnityEngine;
/// <summary>
/// Async awaitable UnityWebRequest <br/><br/>
/// Usage example: <br/><br/>
/// UnityWebRequest www = new UnityWebRequest(); <br/>
/// // do unitywebrequest setup here here... <br/>
/// await www.SendWebRequest(); <br/>
/// Debug.Log(req.downloadHandler.text); <br/>
/// </summary>
public struct UnityWebRequestAwaiter : INotifyCompletion
{
private UnityWebRequestAsyncOperation asyncOp;
private Action continuation;
public UnityWebRequestAwaiter(UnityWebRequestAsyncOperation asyncOp)
{
this.asyncOp = asyncOp;
continuation = null;
}
public bool IsCompleted { get { return asyncOp.isDone; } }
public void GetResult() { }
public void OnCompleted(Action continuation)
{
this.continuation = continuation;
asyncOp.completed += OnRequestCompleted;
}
private void OnRequestCompleted(AsyncOperation obj)
{
continuation?.Invoke();
}
}
public static class ExtensionMethods
{
public static UnityWebRequestAwaiter GetAwaiter(this UnityWebRequestAsyncOperation asyncOp)
{
return new UnityWebRequestAwaiter(asyncOp);
}
}
if continuation is null, it's possible it could be called while null, so OnRequestComplete should accommodate a nullcheck.
It is no possible, because subscription made after set of parameter in OnCompleted.
this.continuation = continuation;
asyncOp.completed += OnRequestCompleted;
After performance and allocation`s tests we made this minimalistic version:
[ DebuggerNonUserCode ]
public readonly struct AsyncOperationAwaiter : INotifyCompletion
{
private readonly AsyncOperation _asyncOperation;
public bool IsCompleted => _asyncOperation.isDone;
public AsyncOperationAwaiter( AsyncOperation asyncOperation ) => _asyncOperation = asyncOperation;
public void OnCompleted( Action continuation ) => _asyncOperation.completed += _ => continuation();
public void GetResult() { }
}
[ DebuggerNonUserCode ]
public readonly struct UnityWebRequestAwaiter : INotifyCompletion
{
private readonly UnityWebRequestAsyncOperation _asyncOperation;
public bool IsCompleted => _asyncOperation.isDone;
public UnityWebRequestAwaiter( UnityWebRequestAsyncOperation asyncOperation ) => _asyncOperation = asyncOperation;
public void OnCompleted( Action continuation ) => _asyncOperation.completed += _ => continuation();
public UnityWebRequest GetResult() => _asyncOperation.webRequest;
}
This code and other for async operations in unity could be found in https://github.com/CrazyPandaLimited/SimplePromises
To reduce allocations i suggest to make WebRequestAwaiter as struct and asyncOp.completed += continuation subscribtion only in OnCompleted call. If IsCompleted = true, when asyc state machine will not call OnCompleted but just get the result.
Order of operators in OnCompleted is important, because struct subscribtion make copy of struct in delegate field.