Марк
Марк

Reputation: 11

How to form an asynchronous function with a callback make synchronous function returning variable in Unity

It would seem to be a simple question, but I can't find an answer to it.

I have a program in Unity that saves data locally using standard unity functions PlayerPrefs.SetInt and PlayerPrefs.GetInt.

I want to change saving data from a local computer to saving on a web environment. There is an API and functions for saving on Web. I want to make function that have similar syntax, for example, myPlayerPrefs.SetInt and myPlayerPrefs.GetInt.

But, API dial with the Web asynchronous, and to get a value from the Web, I need to use the Callback function.:

APIController.WebAppStorageGet(key, callback);

For example

APIController.WebAppStorageGet(key, (value) => { Debug.Log( value);}); 

Other example how functions work

// save data
APIController.WebAppStorageSet("score", 5);

// get data
APIController.WebAppStorageGet("score",  (value) => { Debug.Log( value);}); 

The result on the console will be 5

How can I create a class and function:

m_Score = myPlayerPrefs.GetInt("score", 0);

Based on function

APIController.WebAppStorageGet(key, callback);

If I do like this get 0:

public static async Task<int> GetInt(string key, int fallback = 0)
{
    int intvalue = fallback;
    APIControler.WebAppStorageGet(key,  (strvalue) => { 
        intvalue = int.Parse(strvalue);
    });

    return intvalue;
}

Upvotes: -2

Views: 44

Answers (1)

derHugo
derHugo

Reputation: 90814

As mentioned before you can't/shouldn't simply force asynchronous code into a synchronous code pattern. Personally I see nothing wrong in having a callback and handle the result event driven.

The closest alternative would be a Task<int> as you tried - but if course you have to actually wait for a result before returning the fallback value ;)

So if there is no way to modify the given API signature itself you could wrap it into a proper async Task like e.g.

public static async Task<int> GetInt(string key, int fallback = 0)
{
    var completionSource = new TaskCompletionSource<int>();

    try
    {
        APIControler.WebAppStorageGet(key,  strvalue => 
        { 
            var value = int.Parse(strvalue));
            completionSource.SetResult(value);
        }      
    }
    catch
    {
        // you could of course also be more fine grained here
        // and do the fallback only if the API answers with an "expected" error
        // and rather do a SetException here
        completionSource.SetResult(fallback);
    }

    completionSource.Task;
}

Read more about TaskCompletionSource!


if you really wanted you could of course also freeze up the main thread until you receive a value - probably not recomendable, but since you specifically asked for it

public static int GetInt(string key, int fallback = 0)
{
    var completionSource = new TaskCompletionSource<int>();

    try
    {
        APIControler.WebAppStorageGet(key,  strvalue => 
        { 
            var value = int.Parse(strvalue));
            completionSource.SetResult(value);
        }      
    }
    catch
    {
        // you could of course also be more fine grained here
        // and do the fallback only if the API answers with an "expected" error
        // and rather do a SetException here
        completionSource.SetResult(fallback);
    }

    var task = completionSource.Task;

    // This forces the current thread to wait until there is a result for that task
    return task.Result;
}

Could of course use direct while loop waiting for a flag to be set etc - there are multiple ways - but first of all I already had the completionSource code at hand and am lazy ^^ and second going through Task.Result is a good way to make the thread wait without using a CPU heavy while loop

Upvotes: 0

Related Questions