NavidK0 / SimpleGraphQL-For-Unity

A simple graphQL client that allows one to use .graphql files (or code) for queries, mutations, and subscriptions with Unity.
MIT License
34 stars 18 forks source link

How to use the SendAsync with coroutine #2

Closed steuraa closed 4 years ago

steuraa commented 4 years ago

In the readme it says that the library is usable with coroutines with callback, would you mind adding an example on how to achieve this for those amongst us with less in depth C# knowledge?

Also, is there a advantage of using coroutines over async/await? I read in the Unity manual that using async/await is discouraged, but don't know how or if this applies to this situation?

NavidK0 commented 4 years ago

Generally speaking, you want to perform networking tasks off the main thread. Coroutines achieve this by performing work over time by polling code, but ultimately coroutines were just a workaround for the lack of async/await in the past.

You do not have to use coroutines with this library, but you do need a way to "delegate" results over to code that exists on the main thread if you use async/await. There are many ways to do this, and it will highly depend on your game.

In order to use async/await with Unity code directly: Check out UnityMainThreadDispatcher https://github.com/PimDeWitte/UnityMainThreadDispatcher It is a way to queue up method calls to be executed on the main thread. A very straightforward way to transfer results to the main thread.

In order to use this library with Coroutines, you need to add a CustomYieldInstruction. This is also pretty straightforward:

using System;
using System.Threading.Tasks;
using UnityEngine;

public class WaitForAsync : CustomYieldInstruction
{
    private bool _taskDone = false;

    public override bool keepWaiting => !_taskDone;

    public WaitForAsync(Func<Task> func)
    {
        RunAsyncFunc(func);
    }

    private async void RunAsyncFunc(Func<Task> func)
    {
        await func.Invoke();
        _taskDone = true;
    }
}

Then, in the Coroutine, you can call:

yield return new WaitForAsync(/*async func*/);

inside the coroutine and it will wait like normal. This shouldn't require dispatching to the main thread since the coroutine handles it.

I will probably add some documentation to the front page if/when I have some free time. I can probably throw in a CustomYieldInstruction as well so one doesn't have to make their own.

NavidK0 commented 4 years ago

Alright, I've pushed a new update (a1bb2adcb9a94e4fd74abd2ca3671c9733a02460) that contains a custom yield instruction for coroutines. More information is on the README in the Coroutines section.

asteurba commented 4 years ago

Wow thanks for the detailed response! I'll probably stick to async/await as it's more familiar to me (coming from JS background), but might coming in handy for others and I've learned something new in the process!