I have the following code:
// Get all of the files from the local storage directory.
var files = await folder.GetFilesAsync();
// Map each file to a stream corresponding to that file.
var streams = files.Select(async f => { return await f.OpenStreamForWriteAsync(); });
I would expect streams to be of type IEnumerable<Stream> but in fact it is of IEnumberable<Task<Stream>>, which is what I would've expected had I omitted the await keyword. The return type of OpenStreamForWriteAsync is Task<Stream> — surely awaiting it should produce a Stream?
So, why is the return await statement returning a Task?
Thanks for your help.
All async methods return either void, Task, or Task<TResult>. The lambda is just an anonymous method, and thus that still applies. It's essentially the same as this named method:
private static async Task<Stream> Foo(TypeGOesHere f )
{
return await f.OpenStreamForWriteAsync();
}
In order to make it return a Stream it would need to be a blocking method, rather than an async method:
private static Stream Foo(TypeGOesHere f )
{
return f.OpenStreamForWriteAsync().Result;
}
You probably don't want that.
You can turn your IEnumerable<Task<Stream>> into a Task<Stream[]> by using Task.WhenAll if that helps you:
Task<Stream[]> resultTask = Task.WhenAll(streams);
Wouldnt this be the best solution?
// Get all of the files from the local storage directory.
var files = await folder.GetFilesAsync();
// Map each file to a stream corresponding to that file and await the Task that waits for all tasks to complete? maybe thats whats being implied above...
var streams = await Task.WhenAll(files.Select(async f => { return await f.OpenStreamForWriteAsync(); }));
Related
I am running synchronous code. However I need to call a task from a third party library I consume (and hence no control over it), and wait for it to be completed. The task does not have return value but it does update an object passed in as a parameter.
SerializaAsync is defined as
public async Task SerializeAsync(Type type, object value, Stream writeStream,
CancellationToken cancellationToken)
I need to read the memory stream after the task completes. However since the task does not return a Task<T> the GetAwaiter.GetResult() is not valid.
So what I cannot do is something like
var result = resusmbResponseSerializer.SerializeAsync(
typeof(CustomHttpResponse),
smbDeliveryResponse,
memoryStream, // pass a new MemoryStream() and task will update the stream with content
CancellationToken.None).GetAwaiter().GetResult()
So I tried to do this.
string responseJson = null;
smbResponseSerializer.SerializeAsync(
typeof(CusometHttpResponse),
smbDeliveryResponse,
memoryStream, // pass a new MemoryStream() and task will update the stream with content
CancellationToken.None).GetAwaiter().OnCompleted(() =>
{
memoryStream.Seek(0, SeekOrigin.Begin);
using (StreamReader sr = new StreamReader(ms))
{
responseJson = sr.ReadToEnd();
}
});
string readValue = responseJson; // this gets executed before the OnCompletedAction completes.
This does not work because the task may not have completed and onCompleted is not yet called. Hence readValue is null (responseJson value at that time).
I could wait for the task using task.wait but then I want to avoid the aggregateexception
is there a better way to wait for this task to be completed and memory stream can be read synchronously?
You can still use GetAwaiter().GetResult() with non-generic Tasks, in which case the GetResult method returns void. So you just need to remove the var result = from your statement:
resusmbResponseSerializer.SerializeAsync(typeof(CustomHttpResponse),
smbDeliveryResponse, memoryStream, CancellationToken.None).GetAwaiter().GetResult()
// At this point the memoryStream has been updated
Admittedly a method named GetResult that returns void is unconventional. Probably is was named this way for consistency with the generic Task<TResult>.
If you wish to await a Task synchronously then I would suggest the following extension method for that:
public static class AsyncHelper
{
private static readonly TaskFactory TaskFactory =
new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);
public static void RunSync(this Func<Task> func)
=> TaskFactory
.StartNew(func).Unwrap()
.GetAwaiter().GetResult();
}
The usage would look like this:
var serializeAsync = async () =>
await resusmbResponseSerializer.SerializeAsync(
typeof(CustomHttpResponse),
smbDeliveryResponse,
memoryStream,
CancellationToken.None);
serializeAsync().RunSync();
//Process memorystream
Im trying to understand how Lambda expression work with async methods.
I have a function
private int Server_Get_Int(){
Task<int> task = Task.Factory.StartNew<int>( async () => {
FirebaseClient c = Server_Connect();
FirebaseResponse response = await c.GetAsync("todos/set");
return response.ResultAs<int>(); //The response will contain the data being retreived
} );
task.Wait();
int result = task.Result;
Console.WriteLine(result);
return result;
}
I want my async code to run in the lambda expression and get back the result from the server.
But i get back the error:
error CS4010: Cannot convert async lambda expression to delegate type 'Func<int>'. An async lambda expression may return void, Task or Task<T>, none of which are convertible to 'Func<int>'.
It says i can only return a void, task or task<> and to my understanding im returning
task<int>
Is this a problem with what im returning or is this because of async lambda?
Thanks
Edit:
response.ResultAs<int>()
Returns an Int but being inside a Task function it should be returned as a Task
Your whole method is suboptimal. You could rewrite your code to be much simpler. A few comments on your existing code first, however.
You're using Task.Factory.StartNew(), which is dangerous. In
most cases you should simply use Task.Run()
You're using Task.Wait() and Task.Result, which is also suboptimal, not to mention, that Task.Result includes Task.Wait() when accessing it. But I guess, that you want to test the lambda, so it's ok here.
The ResultAs<T>() method converts the response into an int (in your case). The method itself is defined as public virtual T ResultAs<T>(). It needn't return a Task<int>, because your lambda is asynchronous. If you'd remove the async from the lambda you'd have to return a Task<int>, but you can'T do that by simply changing the ResultAs<T> to ResultAs<Task<int>>, you'd have to use a TaskCompletionSource.
Based on the above that we can rewrite your method to this:
private int Server_Get_Int(){
var task = Task.Run(async () => {
var c = Server_Connect();
return (await c.GetAsync("todos/set")).ResultAs<int>();
});
int result = task.Result;
Console.WriteLine(result);
return result;
}
A more concise approach could look like this:
private async Task<int> Server_Get_Int_Async(){
return await Task.Run(async () => {
var c = Server_Connect();
return (await c.GetAsync("todos/set")).ResultAs<int>();
});
}
This creates a new task via Task.Run() and returns that to be completed later.
Based on the comments here are tow ways how you'd call the Server_Get_Int_Asnyc() method. I used explicit types so you can follow my comment, but in almost any case it's better to use var, because the compiler than can choose the best type for the job.
public async Task Foo()
{
// This is the fetch task that's going to be completed sometime in the future. You should almost in any case use configure await on your tasks. For reasons see below.
Task<int> intTask = Server_Get_Int_Async().ConfigureAwait(false);
// Do something other with the task object
// Finally await it and print
int result = await intTask;
Console.WriteLine(result);
}
// Do this if you just need the result and nothing else.
public async Task Bar()
{
int result = await Server_Get_Int_Async().ConfigureAwait(false);
Console.WriteLine(result);
}
In the end it seems, you're pretty new to Task based programming with async/await. I recommend you read the (excellent) introduction article written by Stephen Cleary and go on from there.
The beauty of async/await is, that it propagates naturally through your code and you can write asynchronous code almost like you'd write synchronous code.
Also, placing another article here on why you shouldn't use Wait() or Result to simply get the return value of the async method, since it's going to be noticed much better:
https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
Test code (console app). This correctly shows "Result: 10".
static void Main(string[] args)
{
Func<Task<int>> func = async () => { await Task.Delay(1000); return 10; };
var task = Task.Factory.StartNew(func);
task.Wait();
int result = task.Unwrap().Result;
WriteLine($"Result: {result}");
ReadKey(true);
}
Essentially I'm trying to be able to do this:
var thingTasks = thingFactory.GetMyThings();
// ...
var things = await thingTasks;
I'm trying to start from a list of objects, iterate through that list making an async call for each one, and returning the set of results in an await-able way so the consumer can choose when to await it. GetMyThings itself uses await before generating the list, so it needs to be async itself and is something like:
public async Task<List<Thing>> GetMyThings() {
var thingMakers = await GetThingMakers();
var things = thingMakers.Select(async thing => await thing.GetAsync());
return things;
}
The basic idea is that I have some await lines, then after that I use the results of those lines to generate a list and generating each item also requires an async call. I'm trying to avoid blocking within the method (e.g. .Result) and instead pass that responsibility/opportunity back to the caller. Basically, start the tasks in the list but not await them. This naturally makes me want to return Task<List<Thing>> or 'List>`.
The closest I got was return Task.WhenAll(things) but that didn't work (it needed to be Task<Task<Thing[]>> and await await GetMyThings(). Alternatively, return Select(...) returning a Task<List<Task<Thing>>> and needing a await Task.WhenAll(await GetMyThings()) on the consuming side.
In both cases one needs double await statements to realize the list. I'm thinking it's impossible but is there a way to avoid the double await?
Use Task.WhenAll to await all task at once. This way you will run each GetAsync approximately at the same time. So :
Start all task
Await all
Return task's results
Like this :
public async Task<List<Thing>> GetMyThings()
{
var thingMakers = await GetThingMakers();
var tasks = thingMakers.Select(thing => thing.GetAsync());
var things = await Task.WhenAll(tasks);
return things.ToList();
}
If you want to make the inner tasks await-able outside, you need to actually return them:
public async Task<List<Task<Thing>>> GetMyThings() {
var thingMakers = await GetThingMakers();
var things = thingMakers.Select(thing => thing.GetAsync());
return things.ToList();
}
You can then use this call like this:
List<Task<Thing>> thingTasks = await GetMyThings();
await Task.WhenAll(thingTasks);
List<Thing> = thingTasks.Select(t => t.Result).ToList();
Or even:
List<Thing> things = await GetMyThings()
.ContinueWith(async r =>
{
await Task.WhenAll(r);
return r.Select(r => r.Result).ToList();
});
I have an async method which works when I call it as follows:
var result = await someClass.myAsyncMethod(someParameter);
Is it possible to do something like this, but in one line of code?
var task = someClass.myAsyncMethod(someParameter);
task.RunSynchronously();
var result = task.Result;
Yes you can do it using the Result property directly after the calling of method:
var result = someClass.myAsyncMethod(someParameter).Result;
and a more better way is to wrap it in Task.Run() to avoid deadlocks like:
var result = Task.Run(() => {
return someClass.myAsyncMethod(someParameter);
}).Result;
I also found this RunSynchronously() method on MSDN, but that won't server your question as you want a on liner simple code.
If you find yourself doing this often you can write a small extension
public static class TaskExtensions
{
public static T SyncResult<T>(this Task<T> task)
{
task.RunSynchronously();
return task.Result;
}
}
Then you can use:
var result = Task.SyncResult();
Have a look at this code:
private async void Lista()
{
var _folder = Windows.Storage.ApplicationData.Current.LocalFolder;
var file = await _folder.GetFileAsync("thefile.txt");
var read = await Windows.Storage.FileIO.ReadTextAsync(file);
}
Since the codeblock contais await i need to use async in the signature. This means that I cant simply add "Retrun read" at the end. (which is what i would like to get back from the method.)
From what I can understand i need to use task somehow. Any tips on how to retrieve the var read?
You can change the returns type as Task<string>
private async Task<string> Lista()
{
var _folder = Windows.Storage.ApplicationData.Current.LocalFolder;
var file = await _folder.GetFileAsync("thefile.txt");
var read = await Windows.Storage.FileIO.ReadTextAsync(file);
return read;
}
From MSDN
An async method can have a return type of Task, Task<TResult>, or void. [...]
You specify Task<TResult> as the return type of an async method if the return statement of the method specifies an operand of type TResult. You use Task if no meaningful value is returned when the method is completed. That is, a call to the method returns a Task, but when the Task is completed, any await expression that's awaiting the Task evaluates to void.
You need to change your return type to Task of T where T is your intended return type, in this case string.
private async Task<string> Lista()
{
var _folder = Windows.Storage.ApplicationData.Current.LocalFolder;
var file = await _folder.GetFileAsync("thefile.txt");
var read = await Windows.Storage.FileIO.ReadTextAsync(file);
return read;
}
If you change your return method signature to
private async Task<T> Lista()
Where T is the type returned by ReadTextAsync method then you should be able to return it.
It will then have to be awaited by the invoking method. Or the invoking method will have to "unwrap" it using .Result.
So assuming that ReadTextAsync returns string you can try something like that:
private async Task<string> Lista()
{
var _folder = Windows.Storage.ApplicationData.Current.LocalFolder;
var file = await _folder.GetFileAsync("thefile.txt");
return await Windows.Storage.FileIO.ReadTextAsync(file);
}