Reputation: 34629
I seem to be getting a TaskCanceledException
whenever I return another Task synchronously instead of awaiting it, following the guidelines in When at last you await.
public static class Download
{
public static Task<byte[]> FromYouTubeAsync(string videoUri)
{
using (var client = new HttpClient())
{
return FromYouTubeAsync(
() => client
.GetStringAsync(videoUri),
uri => client
.GetByteArrayAsync(uri));
}
}
public async static Task<byte[]> FromYouTubeAsync(
Func<Task<string>> sourceFactory, Func<string, Task<byte[]>> downloadFactory)
{
string source = await // TaskCanceledException here
sourceFactory()
.ConfigureAwait(false);
// find links
return await
downloadFactory(links.First())
.ConfigureAwait(false);
}
}
Here, the first overload of the method's signature is changed to async, and it awaits the second overload. For some reason, this prevents the TaskCanceledException
.
public static class Download
{
public async static Task<byte[]> FromYouTubeAsync(string videoUri)
{
using (var client = new HttpClient())
{
return await FromYouTubeAsync(
() => client
.GetStringAsync(videoUri),
uri => client
.GetByteArrayAsync(uri));
}
}
public async static Task<byte[]> FromYouTubeAsync(
Func<Task<string>> sourceFactory, Func<string, Task<byte[]>> downloadFactory)
{
string source = await // No exception!
sourceFactory()
.ConfigureAwait(false);
// find links
return await
downloadFactory(links.First())
.ConfigureAwait(false);
}
}
Why does this happen and what can I do to fix it (besides awaiting the method, which wastes resources as described in the link above)?
Upvotes: 2
Views: 1073
Reputation: 457402
Sorry, the link you posted is about applying an optimization which is only applicable if the method does nothing after its await
. To quote the post:
In this case, however, we’re being handed a task to represent the last statement in the method, and thus it’s in effect already a representation of the entire method’s processing...
In your example, the task does not represent the last statement in the method. Look again:
public async static Task<byte[]> FromYouTubeAsync(string videoUri)
{
using (var client = new HttpClient())
{
return await FromYouTubeAsync(...);
}
}
There's something happening after the await
: specifically, the disposing of client
. So the optimization mentioned in that blog post does not apply here.
This is why you're seeing an exception if you try to return the task directly:
public static Task<byte[]> FromYouTubeAsync(string videoUri)
{
using (var client = new HttpClient())
{
return FromYouTubeAsync(...);
}
}
This code is starting the download, then disposing the HttpClient
, and then returning the task. HttpClient
will cancel any outstanding operations when it is disposed.
The code using await
will (asynchronously) wait for the HTTP operation to complete before it disposes the HttpClient
. That is the behavior you need, and await
is the cleanest way to express it. In this case, it's not a "waste of resources" at all, because you have to defer disposing until after the download completes.
Upvotes: 7