33

I'm building a Metro App.

In the MainPage.xaml.cs, I instantiate Album as follows:

Album album = new Album(2012); //With the album ID as its parameter. ListView1.ItemsSource = album.Songs; 

In the Album.cs, the constructor is as follows:

public Album(int ID) { this.ID = ID; Initialize(); //Serves as a wrapper because I have to call httpClient.GetStreamAsync() and "async" doesn't work for the constructor. } 

Finally, the Initialize method:

private async void Initialize() { //...some code... HttpClient cli = new HttpClient(); Stream SourceStream = await HttpClient.GetStreamAsync("http://contoso.com"); //...some code... this.Songs = Parse(SourceStream); } 

The problem is when it runs to GetStreamAsync, it then goes to ListView1.ItemsSource = album.Songs directly with the album.Songs null.

Is there any quick solution to this problem?

6
  • await doesn't work like that. it starts an asynchronous operation. you need to await for the result in your code. Commented Sep 2, 2012 at 12:34
  • 2
    @Vlad: No, await itself doesn't start the asynchronous operation. await just hands control back and schedules a continuation (if it needs to, of course). Commented Sep 2, 2012 at 12:37
  • @Jon: you're right that it hands the control back and that I ought to have mentioned it, but isn't "schedules a continuation" effectively the same as "starts an asynchronous operation"? Commented Sep 2, 2012 at 12:40
  • 2
    @Vlad: No, it schedules it. The operation doesn't start until whichever operation you're awaiting has completed. Commented Sep 2, 2012 at 12:47
  • 2
    @Vlad: No, because GetStreamAsync is what starts the operation. That could happen a long way before the await expression - in the asynchronous task-based pattern, tasks are "hot", unlike in F# for example. The await expression only schedules the continuation where necessary - it doesn't start the operation. Commented Sep 2, 2012 at 19:37

3 Answers 3

44

Yes. The whole point of async and await are that you don't block. Instead, if you're "awaiting" an operation which hasn't completed yet, a continuation is scheduled to execute the rest of the async method, and control is returned to the caller.

Now because your method has a type of void, you have no way of knowing when that's even finished - if you returned Task (which wouldn't require any change in the body of the method) you'd at least be able to work out when it had finished.

It's not really clear what your code looks like, but fundamentally you should only be trying to set the ItemsSource after initialization has finished. You should probably have your MainPage code in an async method too, which would look something like:

Album album = new Album(2012); ListView1.ItemsSource = await album.GetSongsAsync(); 

Your GetSongs() call would then be:

private async Task<List<Song>> GetSongsAsync() { //...some code... HttpClient cli = new HttpClient(); Stream SourceStream = await HttpClient.GetStreamAsync("http://contoso.com"); //...some code... return Parse(SourceStream); } 

This means Songs would no longer be a property of Album itself, although you could add it in for caching purposes if you wanted to.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for your detailed answer! The problem was solved. But to take it further, is there any alternative to the httpClient.GetStringAsync() method? I mean something like webClient.DownloadString() in the old days. I have to say it's really handy in some ocassions when something like DownloadStringAsync() is not necessary.
@IsilmëOrphousV: It's still in "normal" .NET 4.5 - but not in WinRT, as far as I'm aware... precisely because you should be doing things in an asynchronous way.
The really should have called it something else, beginAsync maybe.
5

Make Songs property return Task<List<Song>> and await at ListView1.ItemsSource = await album.Songs;

4 Comments

But is there any binding-friendly way to accomplish this?
@IsilmëOrphousV What do you mean by binding-friendly way?
@L-B It works fine with List<Song> for I only want to use it for ItemsSource. But if I wanted to bind a Song, which has a property indicating how many times it has been played, a number to be retrieved remotely by asynchronous call, it would be inconvenient to bind the property (Task<int>) to the view. Anyway, I think wrapping it all into a separate method might be a workaround, with more code but more clarity on the other side.
This would mean I'd have to make my calling function async too. Isn't it possible to only make the function containing the HttpClient call async?
0

Many years later... it seems at least worth mentioning that what OP requested was a way to convert an asynchronous call to a synchronous one that waits for a result to return before proceeding, just as in olden times.

Putting a synchronous wrapper on an asynchronous call is easy if you really want/need it. The downside is that it will block the thread on which the synchronous wait executes. Just as in olden times.

It has already been pointed out that usually it is better to modify the design to keep asynchronous things asynchronous. I agree with this sentiment.

The original code did not behave as OP expected because await returns control to the caller (the album constructor) immediately, while scheduling a continuation for the remainder of the code in the initialize() method so that Parse(SourceStream) will execute when the awaited task completes. The accepted answer suggests one way to avoid synchronous waiting and to instead take advantage of the inherent asynchrony of the underlying data request. Other async approaches exist that could preserve the OP's desire to populate the song list within the album constructor. Either way, async all the way down would be a better approach than using a sync wrapper for this application.

Nonetheless, there are situations in which it is actually desirable or necessary to wait synchronously for the results of an async call. Here are two ways to accomplish this in the context of OP's code.

First Method:

// Task.GetAwaiter().GetResult() waits synchronously // and preserves exceptions thrown by the wrapped function. private void Initialize() // no longer async { HttpClient cli = new HttpClient(); // Original code returns control to the caller immediately: // Stream SourceStream = await HttpClient.GetStreamAsync("http://contoso.com"); // Instead do this to block synchronously until the result is available: Task songStreamTask = HttpClient.GetStreamAsync("http://contoso.com"); Stream SourceStream = songStreamTask.GetAwaiter().GetResult(); this.Songs = Parse(SourceStream); } 

Second method:

// Task.Wait() waits synchronously and collects exceptions, // returning them in an Aggregate Exception private void Initialize() // no longer async { HttpClient cli = new HttpClient(); Task songStreamTask = HttpClient.GetStreamAsync("http://contoso.com"); Stream SourceStream = songStreamTask.Wait(); this.Songs = Parse(SourceStream); } 

Both examples shown will block the thread until the task is complete, returning the result just as if GetStreamAsync were a synchronous call. Most of the time this is not something you want to do. If you allow the UI thread to execute a blocking call to a web method, you may find out exactly why async methods were invented. Be careful what you ask for...

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.