Why is an async method with await keyword still blocking the main thread?

Can anyone explain to me the difference between these 2 async methods?

Method A

public async Task<List<Thumbnail>> GetAllThumbnailsAsync()
{
    return await Task.Factory.StartNew(() =>             
    {
        var imageUris = GetAllDirectoriesWithImageAsync(CommandBaseUri).Result;

        return imageUris.Select(GetThumbnail).OrderByDescending(t => t.ImageDateTime).ToList();
    });
}

Method B

public async Task<List<Thumbnail>> GetAllThumbnailsAsync()
{
    var imageUris = await GetAllDirectoriesWithImageAsync(CommandBaseUri);

    return imageUris.Select(GetThumbnail).OrderByDescending(t => t.ImageDateTime).ToList();           
}

To my understanding both methods should return to the caller and does not block the UI thread, but in this case only Method A works as expected while Method B blocks my UI.

I believe there must be some fundamental concept that I may have misunderstood in the usage of async/await.

Can anyone enlighten me?

Jon Skeet
people
quotationmark

Method A is basically executing everything in a separate task, which will probably end up in a new thread. When you await the resulting task, you won't block anything.

Method B starts by calling GetAllDirectoriesWithImageAsync, and awaits the result. That means while the asynchronous operation is processing, you won't be blocking the UI thread - but by default, when you await the task, that means the continuation will run in the UI thread. So the imageUris.Select(...).OrderByDescending(...).ToList() will run in the UI thread, and I suspect that's the part that's causing problems in the UI.

Now you could call .ConfigureAwait(false) at the end of the first line of GetAllThumbnailsAsync, to indicate that you don't need to execute the second part on the UI thread - but that wouldn't guarantee that you wouldn't execute the second part on the UI thread. Instead, it seems to me that you really want an asynchonous way of getting thumbnails. Then you could do something like:

public async Task<List<Thumbnail>> GetAllThumbnailsAsync()
{
    var imageUris = await GetAllDirectoriesWithImageAsync(CommandBaseUri)
        .ConfigureAwait(false);

    var thumbnailTasks = imageUris.Select(GetThumbnailAsync).ToList();
    var thumbnails = await Task.WhenAll(thumbnailTasks).ConfigureAwait(false);

    // I'm assuming there will be sufficiently few thumbnails here
    // that sorting them on the UI thread wouldn't be a problem, in
    // the unlikely event that we're *really* still on the UI thread by
    // this point...
    return thumbnails.OrderByDescending(t => t.ImageDateTime).ToList();           
}

Note that I'm assuming that all async methods are written reasonably, e.g. that GetAllDirectoriesWithImageAsync doesn't perform a significant amount of synchronous work before returning a task.

people

See more on this question at Stackoverflow