Coroutine vs async method

With Unity 2017.1 we have the opportunity to implement async methods. I never worked with them so im asking myself what are the advantages/disadvantages of Coroutines and async methods?

Is one better than the other?

Both fulfil different needs.
Coroutines are useful for executing methods over a number of frames.
Async methods are useful for executing methods after a given task has finished.

e.g. async methods can commonly used to wait for I/O operations to complete.
Coroutines can be used to move an object each frame.

Unity devs have said they may replace Coroutines with an Async/Await style alternative, but they do not currently have any plans to, especially until they is a distinct reason to.

6 Likes

That makes sense. Lets say i want to Destroy a gameobject after 5 seconds. Is it better to use a coroutine or an async method?

@Reeley Probably a coroutine. No faffing about in other threads and the code will likely be cleaner + more readable.

for that use a CoRoutine since it is all on the main thread and you simply want to wait X amount of time.

I havent dealt with the Task, Task, assync and await in unity yet, but i have for other pure .net or xamarin projects. What they are useful for is waiting on a long running task to complete. Such as things like waiting for the response to a REST API request, and than doing something with that response after it is complete, or really dealing with any long running task that does not need to be on the main thread.

When it comes to unity and doing stuff over time, i would stick with coroutines only since that is what they were made for.

3 Likes

Also, just to note, there is no reason to use a coroutine or async to destroy a gameobject as the destroy call already allows you to specify a time.

7 Likes

Seems like I might be missing something here. I just tried to import System.Threading.Tasks and use the async keyword in Unity 2017.1. The namespace didn’t exist, and I got a compiler error ā€œFeature `asynchronous functions’ cannot be used because it is not part of the C# 4.0 language specificationā€.

Perhaps this is a not-yet implemented feature?

You may have to change it to C# 4.6 in the Player Settings.

2 Likes

Thanks, that did the trick! Now to go experiment with these cool new features.

I wonder if Unity/someone will do a write-up on the new C# 6 features and examples of where to use them? I assume there’s some reason that they made the switch other than async capabilities. Maybe there’s just inherent optimizations that come along with the new compiler?

async/await doesn’t necessarily have to be for running async stuff:

       public void StartTimer()
       {
           DoSomething(() => Console.WriteLine("Hello!"));
       }

       public async void DoSomething(Action callback)
       {
           while (true)
           {
               await Task.Delay(1000);

               callback();
           }
       }

When used this way, it’s pretty similar to coroutines. That said, I believe there’s a bit more overhead than just using coroutines, so YMMV.

I’ve written state machines and states using async/await as I found it pretty handy for those ā€œjust in caseā€ scenarios where I needed to do async stuff in some state.

1 Like

Thats exactly what i wonder about, but i guess your right it will have more overhead because a Task has to be created.

There is a library I made.
It makes async/await and coroutine handled easily.

using Asyncoroutine;

async void Awake()
{
    await new WaitForSeconds(1f);
    Debug.Log("WaitForSeconds");

    await Task.Delay(1000);
    Debug.Log("Delay");

    WWW www = await new WWW("http://google.com");
    Debug.Log(www.text);

    await new WaitForSecondsRealtime(1f);
    Debug.Log("WaitForSecondsRealtime");

    await UnityCoroutine();
    Debug.Log("UnityCoroutine");
}

https://github.com/zsaladin/asyncoroutine

Note : It’s being still developed so it needs test.

General information that might help. Obviously all IHMO:

o Unity isn’t upgrading C# versions to get any of the new features. It’s just general compatibility and bug-fixes.

o The original command/concept is fork and sleep, which is 30 years old. Unity implemented their version as coroutines; C# is finally adding theirs.

o Microsoft products (C#) generate buzz just because. People will talk up any new feature the way they’ll get excited about new Team Fortress skins.

o Sure, running on another thread can speed things up, but you pay for it with race conditions, and they’re the worst.

Instead of thinking that Unity is providing us with this new await command, I think of it as the TV cable channels that just came with the package.

1 Like

I still haven’t managed to use co-routines much, probably too old fashioned. I still drop by these threads to see if there’s anything I’m missing.

For anyone interested, I took a deeper dive into this topic and wrote about my findings here

3 Likes

Enjoyed the write up, thanks. Learned some cool things! It’s good you accounted for Unity’s game time as well.

Good stuff! Just a couple niggly points. You mentioned that yielding null in a coroutine does nothing when in actuality it waits a frame. You also mentioned this being part of C# 4.6. This is the version of the runtime, not the language. async/await is a part of C# 6.0 IIRC.

3 Likes

Indeed a good explanation.

To add some notes, the default logger behind Debug.Log is not necessarily throwing exceptions when it’s called from other threads.

In regards to the second snippet in ā€˜Exception Handling’, it’s common practice to use the async keyword all the way up anyway.

So this:

private void Start()
{
    SomeTask();
}

private async Task SomeTask()
{
    ...
    if ( some boolean expression)
    {
         throw new System.Exception (" ... ");
    }
}

should reallread

private async void Start()
{
   /* await */ SomeTask();
}

That’ll allow the Exception to bubble up correctly.

Or you can now handle it yourself:

private async void Start()
{
    try
    {
        /* await */ SomeTask();
    }
    catch (Exception exc)
    {
         // handle
    }
}

Edit
The /
await */ parts exist because the difference between non-awaiting and awaiting is also significant.

1 Like

Thanks @Suddoha and @KelsoMRK , I made those corrections