2

Been trying to execute tasks sequentially but they are executed in a random order instead.

  • Appending .Unwrap after .ContinueWith doesn't help
  • Returning a Task of T from these methods instead of Task and assigning their result in the caller doesn't work either

Not sure about signature of my methods, whether they should contain async/await or not.

Sequencing tasks :

Task biographies = LoadArtistBiographies(apiKey);
Task blogs = LoadArtistBlogs(apiKey);
Task familiarity = LoadArtistFamiliarity(apiKey);
Task hottness = LoadArtistHottness(apiKey);
Task images = LoadArtistImages(apiKey);

await biographies.ContinueWith(b => blogs);
await blogs.ContinueWith(f => familiarity);
await familiarity.ContinueWith(h => hottness);
await hottness.ContinueWith(i => images);
await images;

Sample of executed methods :

private async Task LoadArtistBiographies(string apiKey)
{
    var parameters = new ArtistBiographiesParameters();
    parameters.SetDefaultValues();
    parameters.ApiKey = apiKey;
    parameters.Id = _artistId;
    ArtistBiographies biographies = await Queries.ArtistBiographies(parameters);
    ItemsControlBiographies.ItemsSource = biographies.Biographies;
}

The Queries.* methods are also asynchronous :

public static async Task<ArtistBlogs> ArtistBlogs(ArtistBlogsParameters parameters)

What is the correct syntax for chaining tasks that themselves are executing asynchronous tasks ?

aybe
  • 15,516
  • 9
  • 57
  • 105

4 Answers4

6

If you want to execute the tasks in a specific order, you should await them directly:

await LoadArtistBiographies(apiKey);
await LoadArtistBlogs(apiKey);
await LoadArtistFamiliarity(apiKey);
await LoadArtistHottness(apiKey);
await LoadArtistImages(apiKey);

This will cause the second task (LoadArtistBlogs) to be scheduled after the first task completes.

Right now, the tasks are executing "in random order" because you've assigned them to Task instances, which allows each to be executed simultaneously.

That being said, I would actually recommend changing your methods around to returning the values, instead of assigning them to the datasource within the method:

private async Task<Biographies> LoadArtistBiographiesAsync(string apiKey)
{
    var parameters = new ArtistBiographiesParameters();
    parameters.SetDefaultValues();
    parameters.ApiKey = apiKey;
    parameters.Id = _artistId;
    var bio = await Queries.ArtistBiographies(parameters);
    return bio.Biographies;
}

You could then write these as:

ItemsControlBiographies.ItemsSource = await LoadArtistBiographiesAsync(apiKey);
// Other methods below, with await as this example

This makes the intent as the logic flows through the async methods a bit more clear, in my opinion.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • The one change I would suggest is start all the tasks up so they are all running simultaneously then await for the result after all have been started, that way you don't need to wait for `LoadArtistBiographies` to return before you start looking for `LoadArtistBlogs` (If the API you are working with allows multiple simultaneous calls, if you are allowed only one call at a time Reed's way would be the correct way to do it). – Scott Chamberlain Jun 17 '13 at 17:29
  • 1
    @ScottChamberlain The whole premise of the question is that he was doing as you described and executing them all in parallel before waiting on all of them *and that wasn't what he wanted*. – Servy Jun 17 '13 at 17:30
  • @ScottChamberlain The OP had that original, but explicitly said that he *wanted* things to run in order (not sure why). I do agree that, in general, I would start the tasks, then await in order, though. From the post: "Been trying to execute tasks sequentially" – Reed Copsey Jun 17 '13 at 17:30
  • @Servy that is why I added this as a comment and not a answer, it was more of a "Hey just be aware of this" thing if he ever decides to change from a asynchronous sequence of tasks to a parallel set of tasks so he doesn't need to come back with a 2nd question about "How do I start multiple async tasks at the same time". I see too often people think you **MUST** put `await` on the same line you call a `async` function. – Scott Chamberlain Jun 17 '13 at 17:33
  • When executing them all at the same time, the UI *seems* frozen as nothing happens until a few seconds. Executing them sequentially looks more elegant, as they happen one by one and the UX is less boring as there's something to look at while waiting for images, which is by far the longest to wait for. – aybe Jun 17 '13 at 17:42
2

Your example code will start executing all the tasks without waiting for each one to complete. It then waits for them to complete in order.

The key is that an async method starts when you call it. So if you don't want to start it yet, don't call the method yet:

await LoadArtistBiographies(apiKey);
await LoadArtistBlogs(apiKey);
await LoadArtistFamiliarity(apiKey);
await LoadArtistHottness(apiKey);
await LoadArtistImages(apiKey);
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • I suppose it is also possible to use `var task = Task.Run(()=>LoadArtist(apikey)); await task` in each case? – Gerard Feb 12 '14 at 16:25
  • @Gerard: You should only use `Task.Run` if you want to offload to a background thread. If you have a naturally-asynchronous operation (like loading artists from a server), then you should use an asynchronous implementation rather than `Task.Run`. – Stephen Cleary Feb 12 '14 at 16:37
  • I am afraid I don't understand the difference: 'offload to a background thread' and 'asynchronous implementation'. Just asked a related question (awaited task does not handle exception gracefully). – Gerard Feb 12 '14 at 17:20
  • @Gerard: An asynchronous implementation does not block a thread while the operation is in progress. I have a [blog post](http://blog.stephencleary.com/2013/11/there-is-no-thread.html) describing how `async` calls avoid blocking threads. – Stephen Cleary Feb 12 '14 at 17:57
  • I like how you start your blog: "This is an essential truth of async in its purest form: There is no thread." – Gerard Feb 12 '14 at 20:28
1

await will wait for the given task to complete, it will not start the task. Your Load*-methods all most likely start a task. All five tasks are running in an arbitrary order.

At the point when you get to await, your task may already has finished or not. It does not matter. You call ContinueWith on it, telling your task it should continue with this method once finished. This will return a new Task, on which you finally await.

user1908061
  • 616
  • 5
  • 12
0

Actually I've just found a way but without ContinueWith :

ArtistBiographies biographies = await LoadArtistBiographies(apiKey);
ItemsControlBiographies.ItemsSource = biographies.Biographies;

ArtistBlogs blogs = await LoadArtistBlogs(apiKey);
ItemsControlBlogs.ItemsSource = blogs.Blogs;

ArtistFamiliarity familiarity = await LoadArtistFamiliarity(apiKey);
ContentControlFamiliarity.Content = familiarity.artist;

ArtistHotttnesss hottness = await LoadArtistHottness(apiKey);
ContentControlHottness.Content = hottness.Artist;

ArtistImages images = await LoadArtistImages(apiKey);
ItemsControlImages.ItemsSource = images.Images;

Curious if someone could provide the answer using ContinueWith.

aybe
  • 15,516
  • 9
  • 57
  • 105