We want to execute invoke 10 tasks in parallel and handle each of the 10 results in parallel.
to achieve, created a list of tasks and used continuewith each of which are associated to async methods,
snippet
private async Task<List<bool>> TransformJobsAsync(
List<T> jobs)
{
var result = new List<bool>() { true };
var tasks = new List<Task<bool>>(jobs.Count);
try
{
foreach (var j in jobs)
{
tasks .Add(InvokeSomeAsync(j).ContinueWith(x => HandleResultAsync(x, j)).Unwrap());
}
await Task.WhenAll(tasks);
return tasks.Select(x => x.Result).ToList();
}
catch (Exception ex)
{
result = new List<bool>() { false };
}
return result;
}
Task<(T response, T job)> InvokeSomeAsync (T job)
{
var cts = new CancellationTokenSource();
try
{
cts.CancelAfter(30000);
var response = await SomeThirdPartyApi(request, cts.Token);
if (response.HttpStatusCode == System.Net.HttpStatusCode.OK)
{
}
return (response, job);
}
catch (OperationCanceledException opexException)
{
contextMessage = Messages.TranformationExecutionTimedOut;
}
catch (Exception ex)
{
contextMessage = Messages.UnHandledException;
}
finally
{
cts = null; //why? suggested pattern? review.
}
return await Task.FromException<(response, T Job)>(
throw new Exception());
}
async Task<bool> HandleResultAsync(Task<(T response, T job)> task, T job)
{
try
{
if (task.Status == TaskStatus.RanToCompletion)
{
if (task.Result.Status)
{
response = await CallMoreAsync(task.Result.reponse,
job, currentServiceState);
}
else
{
//log returned response = false
}
}
else
{
//log task failed
}
}
catch (Exception ex)
{
response = false;
}
finally
{
await DoCleanUpAsync();
}
return response;
}
I wanted to know if there is any better pattern and continuewith is not appropriate to use!
Sometimes We get this error, System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error)
You should not use ContinueWith
. ContinueWith
is a low-level, dangerous way to do the same thing as await
. Modern code should use await
instead.
To combine two asynchronous operations (e.g., InvokeSomeAsync
and HandleResultAsync
), introduce an async
method:
async Task<bool> InvokeAndHandleResultAsync<T>(T job)
{
var task = InvokeSomeAsync(job);
return await HandleResultAsync(task, job);
}
This can then be used in your foreach
:
foreach (var j in jobs)
{
tasks.Add(InvokeAndHandleResultAsync(j));
}
Other notes:
CancellationTokenSource
should be disposed.await Task.From*
is usually a yellow flag.Task.Status
is a red flag.bool
result with a contextMessage
state somewhere else.I'd write it something like:
async Task InvokeAndHandleResultAsync<T>(T job)
{
using (var cts = new CancellationTokenSource(30000))
{
try
{
var response = await SomeThirdPartyApi(request, cts.Token);
if (!response.Status)
{
//log returned response = false
return;
}
await CallMoreAsync(response, job, currentServiceState);
}
catch (Exception ex)
{
//log task failed
}
finally
{
await DoCleanUpAsync();
}
}
}
Also, instead of building a list of tasks, you can simplify that code:
private async Task TransformJobsAsync(List<T> jobs)
{
return Task.WhenAll(jobs.Select(j => InvokeAndHandleResult(j)));
}