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()
) ?
Use the Result property on the asynchronous Task, like so: // Synchronous method. void Method()
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.
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.
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.
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.
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