I want to convert asynchronous action delegates to asynchronous function delegates that return a specified value. I have come up with an extension method for this:
public static Func<Task<TResult>> Return<TResult>(this Func<Task> asyncAction, TResult result)
{
ArgumentValidate.NotNull(asyncAction, nameof(asyncAction));
return async () =>
{
await asyncAction();
return result;
};
}
However, my extension method is buggy in that exceptions that would have been delivered synchronously from the action delegate now get delivered asynchronously from the function delegate. Concretely:
Func<Task> asyncAction = () => { throw new InvalidOperationException(); };
var asyncFunc = asyncAction.Return(42);
var task = asyncFunc(); // exception should be thrown here
await task; // but instead gets thrown here
Is there a way of creating this wrapper in such a way that synchronous exceptions continue to delivered synchronously? Is ContinueWith the way to go?
Update: A concrete example of an asynchronous operation that throws exceptions synchronously:
public static Task WriteAllBytesAsync(string filePath, byte[] bytes)
{
if (filePath == null)
throw new ArgumentNullException(filePath, nameof(filePath));
if (bytes == null)
throw new ArgumentNullException(filePath, nameof(bytes));
return WriteAllBytesAsyncInner(filePath, bytes);
}
private static async Task WriteAllBytesAsyncInner(string filePath, byte[] bytes)
{
using (var fileStream = File.OpenWrite(filePath))
await fileStream.WriteAsync(bytes, 0, bytes.Length);
}
Test:
Func<Task> asyncAction = () => WriteAllBytesAsync(null, null);
var asyncFunc = asyncAction.Return(42);
var task = asyncFunc(); // ArgumentNullException should be thrown here
await task; // but instead gets thrown here
Well, you won't be able to use async in the initial call. That much is clear. But you can use a synchronous delegate which calls the function, and then captures the returned task to await it inside an async delegate:
public static Func<Task<TResult>> Return<TResult>(this Func<Task> asyncAction, TResult result)
{
ArgumentValidate.NotNull(asyncAction, nameof(asyncAction));
return () =>
{
// Call this synchronously
var task = asyncAction();
// Now create an async delegate for the rest
Func<Task<TResult>> intermediate = async () =>
{
await task;
return result;
};
return intermediate();
};
}
Alternatively, refactor it into two methods, basically extracting the asynchronous lambda expression into an async method:
public static Func<Task<TResult>> Return<TResult>(
this Func<Task> asyncAction, TResult result)
{
ArgumentValidate.NotNull(asyncAction, nameof(asyncAction));
return () =>
{
var task = asyncAction();
return AwaitAndReturn(task, result);
};
}
public static async Func<Task<TResult>> AwaitAndReturn<TResult>(
this Task asyncAction, TResult result)
{
await task;
return result;
}
Related
I have a series of methods (with variable number of parameters) that return a Task
I want to create a method that does something before and after each of this methods, by passing the Task.
I've simplified everything (removed cancellationToken, actual processing, etc) in this sample:
public async Task<string> GetDataString()
{
Console.WriteLine("Executing");
return "test";
}
public async Task<T> Process<T>(Task<T> task)
{
Console.WriteLine("Before");
var res = await task;
Console.WriteLine("After");
return res;
}
And in my main:
Task<string> task = GetDataString();
string result = await Process<string>(tasks);
Console.WriteLine(res);
the console output is
Executing
Before
After
test
What can I do to create the task but not actually starting it? And starting it only before the wait?
I managed to do it by creating a PauseToken, as explained in this article:
https://devblogs.microsoft.com/pfxteam/cooperatively-pausing-async-methods/
but I wonder if is there a better way.
Thanks,
Mattia
Your generic ProcessAsync method could accept a task factory as argument:
public async Task<T> ProcessAsync<T>(Func<Task<T>> taskFactory)
{
Console.WriteLine("Before");
var res = await taskFactory();
Console.WriteLine("After");
return res;
}
This way the task will be created at the time you'll invoke the factory method. You are in control of its creation.
Here is an example of calling the ProcessAsync method, passing as factory a lambda:
var result = await ProcessAsync(() => GetDataStringAsync(arg1, arg2));
This way you are not restricted to a factory method without arguments.
For completeness I should mention that Task objects can also created in a cold state using the constructor new Task(), and started later using the Start method, but this approach is not recommended.
You can remove the async keyword (from GetDataString) and create a new task which will be executed when you await
so the result of the code below is : before , executing , test , after
private static async Task Start()
{
Task<string> task = GetDataString();
string result = await Process<string>(task);
Console.WriteLine(result);
Console.ReadLine();
}
public Task<string> GetDataString()
{
return new TaskFactory(TaskScheduler.Default).StartNew(() =>
{
Console.WriteLine("Executing");
return "test";
});
}
public async Task<T> Process<T>(Task<T> task)
{
Console.WriteLine("Before");
var res = await task;
Console.WriteLine("After");
return res;
}
I got an async method working like an enhanced Task.WhenAll. It takes a bunch of tasks and returns when all are completed.
public async Task MyWhenAll(Task[] tasks) {
...
await Something();
...
// all tasks are completed
if (someTasksFailed)
throw ??
}
My question is how do I get the method to return a Task looking like the one returned from Task.WhenAll when one or more tasks has failed?
If I collect the exceptions and throw an AggregateException it will be wrapped in another AggregateException.
Edit: Full Example
async Task Main() {
try {
Task.WhenAll(Throw(1), Throw(2)).Wait();
}
catch (Exception ex) {
ex.Dump();
}
try {
MyWhenAll(Throw(1), Throw(2)).Wait();
}
catch (Exception ex) {
ex.Dump();
}
}
public async Task MyWhenAll(Task t1, Task t2) {
await Task.Delay(TimeSpan.FromMilliseconds(100));
try {
await Task.WhenAll(t1, t2);
}
catch {
throw new AggregateException(new[] { t1.Exception, t2.Exception });
}
}
public async Task Throw(int id) {
await Task.Delay(TimeSpan.FromMilliseconds(100));
throw new InvalidOperationException("Inner" + id);
}
For Task.WhenAll the exception is AggregateException with 2 inner exceptions.
For MyWhenAll the exception is AggregateException with one inner AggregateException with 2 inner exceptions.
Edit: Why I am doing this
I often need to call paging API:s and want to limit number of simultaneous connections.
The actual method signatures are
public static async Task<TResult[]> AsParallelAsync<TResult>(this IEnumerable<Task<TResult>> source, int maxParallel)
public static async Task<TResult[]> AsParallelUntilAsync<TResult>(this IEnumerable<Task<TResult>> source, int maxParallel, Func<Task<TResult>, bool> predicate)
It means I can do paging like this
var pagedRecords = await Enumerable.Range(1, int.MaxValue)
.Select(x => GetRecordsAsync(pageSize: 1000, pageNumber: x)
.AsParallelUntilAsync(maxParallel: 5, x => x.Result.Count < 1000);
var records = pagedRecords.SelectMany(x => x).ToList();
It all works fine, the aggregate within aggregate is just a minor inconvenience.
async methods are designed to only every set at most a single exception on the returned task, not multiple.
This leaves you with two options, you can either not use an async method to start with, instead relying on other means of performing your method:
public Task MyWhenAll(Task t1, Task t2)
{
return Task.Delay(TimeSpan.FromMilliseconds(100))
.ContinueWith(_ => Task.WhenAll(t1, t2))
.Unwrap();
}
If you have a more complex method that would be harder to write without using await, then you'll need to unwrap the nested aggregate exceptions, which is tedious, although not overly complex, to do:
public static Task UnwrapAggregateException(this Task taskToUnwrap)
{
var tcs = new TaskCompletionSource<bool>();
taskToUnwrap.ContinueWith(task =>
{
if (task.IsCanceled)
tcs.SetCanceled();
else if (task.IsFaulted)
{
if (task.Exception is AggregateException aggregateException)
tcs.SetException(Flatten(aggregateException));
else
tcs.SetException(task.Exception);
}
else //successful
tcs.SetResult(true);
});
IEnumerable<Exception> Flatten(AggregateException exception)
{
var stack = new Stack<AggregateException>();
stack.Push(exception);
while (stack.Any())
{
var next = stack.Pop();
foreach (Exception inner in next.InnerExceptions)
{
if (inner is AggregateException innerAggregate)
stack.Push(innerAggregate);
else
yield return inner;
}
}
}
return tcs.Task;
}
Use a TaskCompletionSource.
The outermost exception is created by .Wait() or .Result - this is documented as wrapping the exception stored inside the Task inside an AggregateException (to preserve its stack trace - this was introduced before ExceptionDispatchInfo was created).
However, Task can actually contain many exceptions. When this is the case, .Wait() and .Result will throw an AggregateException which contains multiple InnerExceptions. You can access this functionality through TaskCompletionSource.SetException(IEnumerable<Exception> exceptions).
So you do not want to create your own AggregateException. Set multiple exceptions on the Task, and let .Wait() and .Result create that AggregateException for you.
So:
var tcs = new TaskCompletionSource<object>();
tcs.SetException(new[] { t1.Exception, t2.Exception });
return tcs.Task;
Of course, if you then call await MyWhenAll(..) or MyWhenAll(..).GetAwaiter().GetResult(), then it will only throw the first exception. This matches the behaviour of Task.WhenAll.
This means you need to pass tcs.Task up as your method's return value, which means your method can't be async. You end up doing ugly things like this (adjusting the sample code from your question):
public static Task MyWhenAll(Task t1, Task t2)
{
var tcs = new TaskCompletionSource<object>();
var _ = Impl();
return tcs.Task;
async Task Impl()
{
await Task.Delay(10);
try
{
await Task.WhenAll(t1, t2);
tcs.SetResult(null);
}
catch
{
tcs.SetException(new[] { t1.Exception, t2.Exception });
}
}
}
At this point, though, I'd start to query why you're trying to do this, and why you can't use the Task returned from Task.WhenAll directly.
I deleted my previous answer, because I found a simpler solution. This solution does not involve the pesky ContinueWith method or the TaskCompletionSource type. The idea is to return a nested Task<Task> from a local function, and Unwrap() it from the outer container function. Here is a basic outline of this idea:
public Task<T[]> GetAllAsync<T>()
{
return LocalAsyncFunction().Unwrap();
async Task<Task<T[]>> LocalAsyncFunction()
{
var tasks = new List<Task<T>>();
// ...
await SomethingAsync();
// ...
Task<T[]> whenAll = Task.WhenAll(tasks);
return whenAll;
}
}
The GetAllAsync method is not async. It delegates all the work to the LocalAsyncFunction, which is async, and then Unwraps the resulting nested task and returns it. The unwrapped task contains in its .Exception.InnerExceptions property all the exceptions of the tasks, because it is just a facade of the internal Task.WhenAll task.
Let's demonstrate a more practical realization of this idea. The AsParallelUntilAsync method below enumerates lazily the source sequence and projects the items it contains to Task<TResult>s, until an item satisfies the predicate. It also limits the concurrency of the asynchronous operations. The difficulty is that enumerating the IEnumerable<TSource> could throw an exception too. The correct behavior in this case is to await all the running tasks before propagating the enumeration error, and return an AggregateException that contains both the enumeration error, and all the task errors that may have occurred in the meantime. Here is how it can be done:
public static Task<TResult[]> AsParallelUntilAsync<TSource, TResult>(
this IEnumerable<TSource> source, Func<TSource, Task<TResult>> action,
Func<TSource, bool> predicate, int maxConcurrency)
{
return Implementation().Unwrap();
async Task<Task<TResult[]>> Implementation()
{
var tasks = new List<Task<TResult>>();
async Task<TResult> EnumerateAsync()
{
var semaphore = new SemaphoreSlim(maxConcurrency, maxConcurrency);
using var enumerator = source.GetEnumerator();
while (true)
{
await semaphore.WaitAsync();
if (!enumerator.MoveNext()) break;
var item = enumerator.Current;
if (predicate(item)) break;
async Task<TResult> RunAndRelease(TSource item)
{
try { return await action(item); }
finally { semaphore.Release(); }
}
tasks.Add(RunAndRelease(item));
}
return default; // A dummy value that will never be returned
}
Task<TResult> enumerateTask = EnumerateAsync();
try
{
await enumerateTask; // Make sure that the enumeration succeeded
Task<TResult[]> whenAll = Task.WhenAll(tasks);
await whenAll; // Make sure that all the tasks succeeded
return whenAll;
}
catch
{
// Return a faulted task that contains ALL the errors!
return Task.WhenAll(tasks.Prepend(enumerateTask));
}
}
}
I read a few threads about TaskCancellations.. However, I cannot find a solution for a simple question: How do I get a default value when my task fails?
I cannot (!) modify the task itself and put a try catch wrapper around it. I could of course put a try-catch around await, but I would like to handle this with ContinueWith - if possible.
public Task<List<string>> LoadExample()
{
Task<List<string>> task = LoadMyExampleTask();
task.ContinueWith(t => default(List<string>), TaskContinuationOptions.OnlyOnFaulted);
return task;
}
I thought this would be the correct way to deal with the problem. However, my application throws a JsonParseException (which is called in LoadMyExampleTask). I would expect to get null or (even better) an empty list.
In fact, all I want is:
var emptyOrFilledList = await LoadExample(); // guaranteed no exception thrown
Based on Luaan's great answer I wrote an extension method with a defaultValue-option:
public static Task<T> DefaultIfFaulted<T>(this Task<T> #this, T defaultValue = default(T))
{
return #this.ContinueWith(t => t.IsCompleted ? t.Result : defaultValue);
}
Edit: await myTask.DefaultifFaulted() just throwed a
[ERROR] FATAL UNHANDLED EXCEPTION: System.AggregateException
Are you sure that every exception is caught?
If you want that, you must not return the original task - you need to return the continuation.
public Task<List<string>> LoadExample()
{
Task<List<string>> task = LoadMyExampleTask();
return task.ContinueWith(t =>
t.IsFaulted || t.IsCanceled ? default(List<string>) : t.Result);
}
Your original code did allow the continuation to run when the original task faulted, but you didn't read the status of that task - the fact that a task has a continuation which handles errors is entirely irrelevant to what an await on the original task will do.
Of course, it's rather easy to make this into a generic helper method:
public static Task<T> DefaultIfFaulted<T>(this Task<T> #this)
{
return #this.ContinueWith (t => t.IsCanceled || t.IsFaulted ? default(T) : t.Result);
}
As promised, here are the DefaultIfFaulted<T> variants which are true to their name (and the title of this question). They preserve the antecedent task's behavior unless it's faulted (specifically, cancellation is propagated rather than ignored or masked by an AggregateException):
Old-school (.NET 4.0) way:
public static Task<T> DefaultIfFaulted<T>(this Task<T> task)
{
// The continuation simply returns the antecedent task unless it's faulted.
Task<Task<T>> continuation = task.ContinueWith(
t => (t.Status == TaskStatus.Faulted) ? Task.FromResult(default(T)) : t,
TaskContinuationOptions.ExecuteSynchronously
);
return continuation.Unwrap();
}
Async/await way (simple but slower):
public static async Task<T> DefaultIfFaulted<T>(this Task<T> task)
{
try
{
return await task.ConfigureAwait(false);
}
catch (Exception ex) when (!(ex is OperationCanceledException))
{
return default(T);
}
}
Async/await way (perf almost identical to Unwrap):
public static async Task<T> DefaultIfFaulted<T>(this Task<T> task)
{
// Await completion regardless of resulting Status (alternatively you can use try/catch).
await task
.ContinueWith(_ => { }, TaskContinuationOptions.ExecuteSynchronously)
.ConfigureAwait(false);
return task.Status != TaskStatus.Faulted
// This await preserves the task's behaviour
// in all cases other than faulted.
? await task.ConfigureAwait(continueOnCapturedContext: false)
: default(T);
}
Tests (passed by all of the above):
using Xunit;
[Fact]
public async Task DefaultIfFaultedTest()
{
var success = Task.Run(() => 42);
var faulted = Task.Run(new Func<int>(() => { throw new InvalidOperationException(); }));
Assert.Equal(42, await success.DefaultIfFaulted());
Assert.Equal(0, await faulted.DefaultIfFaulted());
await Assert.ThrowsAsync<TaskCanceledException>(() =>
{
var tcs = new TaskCompletionSource<int>();
tcs.SetCanceled();
return tcs.Task.DefaultIfFaulted();
});
}
I have a method that takes a Action<String>. When the method finishes its processing it calls the Action<String> with the return value.
MethodWithCallback((finalResponse)=> {
Console.WriteLine(finalResponse);
});
I want to use this in a web.api async controller. How do I wrap this method so I can await for this method to complete in an async manner. I cannot modify the method itself, it is in a legacy code base.
What I would like to be able to do is this
String returnValue = await MyWrapperMethodThatCallsMethodWithCallback();
You can leverage the TaskCompletionSource class and solve the problem in a generic way:
Task<T> AsAsync<T>(Action<Action<T>> target) {
var tcs = new TaskCompletionSource<T>();
try {
target(t => tcs.SetResult(t));
} catch (Exception ex) {
tcs.SetException(ex);
}
return tcs.Task;
}
That way you don't have to modify your MethodWhitCallback:
var result = await AsAsync<string>(MethodWithCallback);
Console.WriteLine(result);
I'm trying to get the hand of the new async CTP stuff and I'm probably confusing myself here..
I can have this "task method", with no problem:
public static Task<String> LongTaskAAsync() {
return Task.Run(() => {
return("AAA");
});
}
But what if I need the task to execute another task, can I mark it as "async" and use "await"? I tried this:
public async static Task<String> LongTaskAAsync() {
await Task.Delay(2000);
return Task.Run(() => {
return("AAA");
});
}
But then mysteriously get this compiler error: Since this is an async method, the return expression must be of type 'string' rather than Task<string>
What am I missing here?
You may want to read my async/await intro post.
Return values from async methods are wrapped in a Task<TResult>. Likewise, await unwraps those return values:
public static async Task<String> LongTaskAAsync() {
await Task.Delay(2000);
return await Task.Run(() => {
return("AAA");
});
}
The reasoning behind this is described in my Async "Why Do the Keywords Work That Way" Unofficial FAQ.
P.S. You can also use Task.FromResult for simple tests like this.
Edit: If you want to create and return the Task object itself, then the method should not be async. One somewhat common pattern is to have a public non-async method that calls the async portion only if necessary.
For example, some kind of asynchronous cache - if the object is in the cache, then return it immediately; otherwise, asynchronously create it, add it to the cache, and return it (this is example code - not thread-safe):
public static Task<MyClass> GetAsync(int key)
{
if (cache.Contains(key))
return Task.FromResult(cache[key]);
return CreateAndAddAsync(key);
}
private static async Task<MyClass> CreateAndAddAsync(int key)
{
var result = await CreateAsync(key);
cache.Add(key, result);
return result;
}
Can a “task method” also be an “async” method?
Yes it can be, by simply changing the method signature to public async static Task<Task<String>> LongTaskAAsync() since that is, what it will return.
If you use the async keyword, the runtime will wrap the type you return into a task, to enable asynchronousness. Say if you return a string, the runtime will wrap that into a Task<string>. int will go Task<int> and Task<string> will go Task<Task<string>>. See this console app to clearify:
public class Program
{
public static void Main(string[] args)
{
// start the main procedure asynchron
Task.Run(() => DoIt()).Wait();
}
// for async support since the static main method can't be async
public static async void DoIt()
{
Program p = new Program();
// use the methods
string s = await p.GetString();
int i = await p.GetInt();
Task<string> tsk = await p.GetTaskOfString();
// just to prove the task works:
// C# 5
string resultFromReturnedTask = await tsk;
// C# 4
string resultFromReturnedTask2 = tsk.Result;
}
public async Task<string> GetString()
{
return "string";
}
public async Task<int> GetInt()
{
return 6;
}
public async Task<Task<string>> GetTaskOfString()
{
return Task.Run(() => "string");
}
}