I've got the following code which seems to run fine apart from the continuation on the WhenAll ... await Task.WhenAll(syncTasks).ContinueWith ... is run before all four methods are completed. Would appreciate any guidance on what I'm doing wrong here. I don't really feel like I understand how to arrange complex async functionality and what seems to be happening supports that. This is in a Xamarin App BTW although I don't suppose that really matters.
private async Task SyncItems()
{
var updateItemOnes = Task.Run(() =>
{
UpdateItemOnesToServer(itemOnesToUpdate).ContinueWith(async (result) => {
if (!result.IsFaulted && !result.IsCanceled)
{
await UpdateItemOnesToLocal(itemOnesToUpdate);
}
});
});
syncTasks.Add(updateItemOnes);
var updateItemTwos = Task.Run(() =>
{
UpdateItemTwosToServer(itemTwosToUpdate).ContinueWith(async (result) => {
if (!result.IsFaulted && !result.IsCanceled)
{
await UpdateItemTwosToLocal(itemTwosToUpdate);
}
});
});
syncTasks.Add(updateItemTwos );
//Show Loading Dialog
await Task.WhenAll(syncTasks).ContinueWith((result) => {
if (!result.IsFaulted && !result.IsCanceled)
{
//Success
}
else
{
//Error
}
//Hide Loading Dialog
});
}
private async Task UpdateItemOnesToServer(IEnumerable<Item> itemOnesToUpdate)
{
try
{
var listofTasks = new List<Task>();
foreach (var item in itemOnesToUpdate)
{
var convertItemOneTask = Task.Run(async () => {
//Convert Image File in Item to Base64 here
});
listofTasks.Add(convertItemOneTask);
}
await Task.WhenAll(listofTasks);
var response = await _apiManager.SaveItemOnes(itemOnesToUpdate);
if (response.IsSuccessStatusCode)
{
//Update ItemOnes for Local Update with Response Values
}
}
catch
{
throw;
}
}
private async Task UpdateItemOnesToLocal(IEnumerable<Item> itemOnesToUpdate)
{
var listOfTasks = new List<Task<bool>>();
foreach (var itemOne in itemOnesToUpdate)
{
listOfTasks.Add(_localService.UpdateItemOne(itemOne));
}
await Task.WhenAll<bool>(listOfTasks);
}
private async Task UpdateItemTwosToServer(IEnumerable<ItemOne> itemTwosToUpdate)
{
try
{
var listofTasks = new List<Task>();
foreach (var item in itemTwosToUpdate)
{
var convertItemTwoTask = Task.Run(async () => {
//Convert Image File in Item to Base64 here
});
listofTasks.Add(convertItemTwoTask);
}
await Task.WhenAll(listofTasks);
var response = await _apiManager.SaveItemTwos(itemTwosToUpdate);
if (response.IsSuccessStatusCode)
{
//Update ItemTwos for Local Update with Response Values
}
}
catch
{
throw;
}
}
private async Task UpdateItemTwosToLocal(IEnumerable<ItemTwo> itemTwosToUpdate)
{
var listOfTasks = new List<Task<bool>>();
foreach (var itemTwo in itemTwosToUpdate)
{
listOfTasks.Add(_localService.UpdateItemTwo(itemTwo));
}
await Task.WhenAll<bool>(listOfTasks);
}
Thanks in advance to anyone who can provide a little clarity. It will be much appreciated.
So there are a few problems with this code.
someTask.ContinueWith(X)Basically this says "once the someTask is completed, do X" (there's more going on but in this case think of it like this). However if you awaitsomeTaskthis will not include theContinueWithpart. So like this theTask.WhenAll(syncTasks)will not wait on yourContinueWithparts.var updateItemOnes = Task.Run(() => UpdateItemOnesToServer())wrappers. There is no awaiting here, so this will create a Task that just starts theUpdateItemOnesToServertask. That is done instantly.If you would like to see what is happening in practice use this test class:
You'll see that the output is as follows:
Notice here the "tasks completed" is called straight after starting the two tasks.
Now if we change the
Runmethod like this I think we'll get the functionality you're looking for:Which will output:
So we see here that Local1 is always after Server1 and Local2 is always after Server2 and "tasks completed" is always after Local1 & Local2
Hope this helps!
Edit: From you comment you said you would like to see any exceptions that occurred in the process. This is where you could use
ContinueWith(it is also fired when exceptions are throw:If you change the following test calls:
This will be your output: