1

I've got 5 tasks like this:

private async Task<List<Places>> GetPlaces()
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    var list5 = await GetPlacesTask();
    sw.Stop();
    Testowybox3.Text = sw.Elapsed.ToString()+" get places times";
    return list5;
}
private async Task<List<Categories>> GetCategories()
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    var list5 = await GetCategoriesTask();
    sw.Stop();
    Testowybox2.Text = sw.Elapsed.ToString()+" get categories times";
    return list5;
}

They differs by returned value for e.g. my second task:

 private async Task<EventsInstance[]> GetDetailedData()
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            list4 = await GetDetailedDataTask();
            List<EventsListPrint> lista = new List<EventsListPrint>();
            foreach (var VARIABLE in list4)
            {
                lista.Add(new EventsListPrint() { Date = string.Format("{0:dd.MM.yyyy}", VARIABLE.startDate.Date), Time = VARIABLE.startDate.TimeOfDay.ToString(), Category = VARIABLE.categoryId.ToString(), Where = VARIABLE.organizer.designation, What = VARIABLE.name });
            }
            fruitdatagrid2.ItemsSource = lista;
            sw.Stop();
            Testowybox.Text = sw.Elapsed.ToString()+" get detailed data times";


            return list4;
        }

        private async Task<List<Organizers>> GetOrganizers()
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            var list5 = await GetOrganizersTask();

            sw.Stop();
            Testowybox4.Text = sw.Elapsed + " get orgzanizers times";
            return list5;
        }

They are parsing Jsons from web. I'm collecting data from them like this:

            var DetailedDataList = await GetDetailedData();
            var CategoriesList = await GetCategories();
            var PlacesList = await GetPlaces();
            var OrganizersList = await GetOrganizers();
            var EventsList = await GetEvents();

How can I assure that they're all done? I tried:

   Task.WhenAll(GetDetailedData(), GetCategories(), GetPlaces(), GetOrganizers(), GetEvents());

but it seems that I cannot retrieve values then. What I really want to achieve is to create method which would be aware of successful completion of tasks and then operarate on this data in my WPF GUI. I could maybe chain them in one, but I just don't know how to do so. Everything works fine as far as I'm using one of the returned list. When trying to do linq on one list using another list, there is a chance that the another one isn't prepared yet and it simply crashes my app.

bc291
  • 1,071
  • 11
  • 23

1 Answers1

2

When you await Task.WhenAll, when the line resumes, you can be assured that the tasks that were supplied will be in a completed state.

var detailedDataTask = GetDetailedData();
var categoriesTask = GetCategories();
//etc
await Task.WhenAll(detailedDataTask, categoriesTask); //etc

However, the variables that contain the tasks (detailedDataTask) are still Task<T>. Not the type that they wrap, T.

How do we unwrap the completed tasks?

We can either (unintuitively, but preferably) await the completed task:

var detailedData = await detailedDataTask;

or (because we know that the tasks are complete) we can go directly for the .Result property without fear of blocking...

var detailedData = detailedDataTask.Result;

Because of the additional magic that happens when the runtime untangles your asynchronous exceptions, you should probably prefer awaiting all the way.

spender
  • 117,338
  • 33
  • 229
  • 351
  • 1
    `await` will behave slightly different from `Result` when it comes to exception handling. – Cory Nelson Mar 26 '18 at 01:05
  • @CoryNelson Yes. Here's more info on that: https://stackoverflow.com/a/24657079/14357 – spender Mar 26 '18 at 01:08
  • When I try to use: await Task.WhenAll(GetDetailedData()); var DetailedDataList = GetDetailedData().Result; it is crashing hard. – bc291 Mar 26 '18 at 01:14
  • @needtobe You're calling `GetDetailedData()` twice and it's running twice. First time round you await it. Second time round you're invoking `.Result` before it's finished. There's a reason why my example above stores the tasks in variables. – spender Mar 26 '18 at 01:15
  • So how can I return my value from just one Task.WhenAll(GetDetailedData()); ? When I'm using just await GetDetailedData().Result; it's still crashing sadly. – bc291 Mar 26 '18 at 01:18
  • @needtobe I'm not sure I understand what you're asking. If your tasks all wrap **the same type**, you can `var resultsArray = await Task.WhenAll(taskThatWrapsListOfSomeType, anotherTaskThatWrapsListOfSomeType)` and your `resultsArray` will contain the unwrapped values (with 2 entries of type `List`). Because all of your tasks return different types, you are restricted to reading them out *after* the `WhenAll` has completed. – spender Mar 26 '18 at 01:22
  • I'm a little bit confused right now. I still don't know how to read them out without running them twice. – bc291 Mar 26 '18 at 01:25
  • @needtobe Look carefully at my examples above. I'm only calling `GetDetailedData()` **once**. I'm storing the task that it returns **in a variable** (`detailedDataTask`). I'm using the value of that variable in `Task.WhenAll` and I'm using **the same variable** when I `await` or `.Result` after the `WhenAll` completes. – spender Mar 26 '18 at 01:27
  • @needtobe So, there's a sequence of code. `await Task.WhenAll(detailedDataTask, categoriesTask);` ***immediately followed*** by `var detailedData = await detailedDataTask; //etc`. – spender Mar 26 '18 at 01:29
  • @needtobe I just noticed above... Don't combine `await` with `.Result` on the same line. It's two **different** ways of unwrapping a completed task. – spender Mar 26 '18 at 01:34