I have a coroutine as follows:
IEnumerator CountingCoroutine()
{
while (true)
{
DoSomething();
yield return new WaitForSeconds(1);
}
}
I need to call some async function within it. If I wrote it as follows, the compiler complains:
async IEnumerator CountingCoroutine()
{
while (true)
{
DoSomething();
await foo();
yield return new WaitForSeconds(1);
}
}
What is the proper wait to call some async functions per second? P.S. I could abandon the whole coroutine function and use update with time counting mechanism, and mark the update function as async. But I'm not sure if it's safe or recommended.
Solution with recommendations from @derHugo:
IEnumerator CountingCoroutine()
{
while (true)
{
DoSomething();
var task = Task.Run(foo);
yield return new WaitUntil(()=> task.IsCompleted);
yield return new WaitForSeconds(1);
}
}
You could use the Task.IsCompleted
like
private IEnumerator CountingCoroutine()
{
while (true)
{
DoSomething();
var task = Task.Run(foo);
yield return new WaitUntil(()=> task.IsCompleted);
}
}
which starts running a new Task as soon as the one before has finished.
You could extend this in order to start a new task earliest about every second or latest when the previous task finishes after a second like e.g.
private IEnumerator CountingCoroutine()
{
var stopWatch = new StopWatch();
while (true)
{
DoSomething();
stopWatch.Restart();
var task = Task.Run(foo);
yield return new WaitUntil(()=> task.IsCompleted);
var delay = Mathf.Max(0, 1 - stopWatch.ElapsedMilliseconds * 1000);
// Delays between 1 frame and 1 second .. but only the rest that foo didn't eat up
yield return new WaitForSeconds(delay);
// or you could with a bit more calculation effort also
// even avoid the one skipped frame completely
if(!Mathf.Approximately(0, delay)) yield return new WaitForSeconds(delay);
}
}
Or if you don't care about the finishing and only want to call this as you say every second then simply do
private IEnumerator CountingCoroutine()
{
while (true)
{
DoSomething();
Task.Run(foo);
yield return new WaitForSeconds(1);
}
}
Note however that this way you lose all control and will never know if and when one of these tasks has finished and might get multiple concurrent tasks running/failing etc
As alternative you could probably also go the other way round and use something like Asyncoroutine) .. you might have to watch out for things landing on a background thread though.
Update
Nowadays I would rather suggest to give UniTask (also available via OpenUPM) a try! It is a lightweight async Task implementation and additionally provides seamless async <-> Unity main thread integrations.
Thanks for the above answers. These codes were helpful for me.
var task = Task.Run(foo);
yield return new WaitUntil(()=> task.IsCompleted);
lobby = t.Result;
But I faced a problem that my async function doesn't work in the Task context because the function code should be run in the main thread.
To solve it, I directly executed the async function in the coroutine instead of the Task context. These are my code to execute the async function in the coroutine.
private IEnumerator TransactionUpdater()
{
...
var task = GetTransactionReceipt(txid);
yield return new WaitUntil(() => task.IsCompleted);
var receipt = task.Result;
...
}
async private Task<TransactionReceipt> GetTransactionReceipt(string txid)
{
try
{
var provider = new JsonRpcProvider(RPC_URL);
return await provider.GetTransactionReceipt(txid);
}
catch (Exception e)
{
return null;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With