Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Async call within synchronous function

I'm trying to populate my cache asynchronously

static ConcurrentDictionary<string, string[]> data = new ConcurrentDictionary<string, string[]>();

public static async Task<string[]> GetStuffAsync(string key)
{
    return data.GetOrAdd(key, async (x) => {
        return await LoadAsync(x);
    });
}

static async Task<string[]> LoadAsync(string key) {....}

but this gives me the error:

Cannot convert async lambda expression to delegate type 'System.Func'.

An async lambda expression may return void, Task or Task, none of which are convertible to 'System.Func'.

As I understand this is because GetOrAdd() is not asynchronous. How can I fix the issue?

Update: LazyAsync suggested in the comments will work in my trivial example. Or, workaround like this (can definitely live with some overhead it introduces):

public static async Task<string[]> GetStuffAsync(string key)
{
    string[] d = null;
    if (!data.ContainsKey(key))
        d = await LoadAsync(key);
    return data.GetOrAdd(key, d);
}

The question then becomes did Microsoft just have no time to update all interfaces to support async or I'm trying to do something deeply wrong (and ConcurrentDictionary shouldn't have GetOrAddAsync()) ?

like image 752
UserControl Avatar asked Jan 21 '15 12:01

UserControl


People also ask

Can we call asynchronous method from another synchronous method?

Use the Result property on the asynchronous Task, like so: // Synchronous method. void Method()

Do async functions run synchronously?

An async method runs synchronously until it reaches its first await expression, at which point the method is suspended until the awaited task is complete. In the meantime, control returns to the caller of the method, as the example in the next section shows.

Can you use await in a synchronous function?

Async/await helps you write synchronous-looking JavaScript code that works asynchronously. Await is in an async function to ensure that all promises that are returned in the function are synchronized.

Is async await asynchronous or synchronous?

Top-level code, up to and including the first await expression (if there is one), is run synchronously. In this way, an async function without an await expression will run synchronously. If there is an await expression inside the function body, however, the async function will always complete asynchronously.


1 Answers

Async methods (or lambda) can only return void or Task or Task<T> but your lambda returns string[] and thus compiler prevents you.

await keyword is optimized to continue synchronously when the Task is already completed. So, one option is to store the Task itself in dictionary and don't worry about awaiting the completed Task again and again.

private static ConcurrentDictionary<string, Task<string[]>> data =
    new ConcurrentDictionary<string, Task<string[]>>();

public static Task<string[]> GetStuffAsync(string key)
{
    return data.GetOrAdd(key, LoadAsync);
}

And when you do

var item = await GetStuffAsync(...);

first time it will (a)wait till the cached Task finishes --There after it will continue synchronously.

You'll have to think about what should happen when LoadAsync fails. Because we're caching the Task returned by LoadAsync; if that fails we'll foolishly cache the failed Task. You may need to handle this.

like image 140
Sriram Sakthivel Avatar answered Sep 23 '22 10:09

Sriram Sakthivel