DownloadStringAsync wait for request completion
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
DownloadStringAsync belongs to the older WebClient API and does not return a Task, so you do not "wait" for it the same way you wait for modern async methods. Instead, it completes through an event callback. In current .NET code, the better answers are usually DownloadStringTaskAsync or, better still, HttpClient with await.
What DownloadStringAsync Actually Does
DownloadStringAsync starts an asynchronous download and returns immediately. Completion is reported through the DownloadStringCompleted event.
That is the original event-based async pattern. There is no return value you can await directly.
If You Need to Wait, Prefer DownloadStringTaskAsync
WebClient also exposes a task-based wrapper that fits modern async code much better.
This is usually the cleanest answer if you are forced to stay on WebClient.
If You Must Block, Block on the Task Version, Not the Event Version
Sometimes legacy console or background code really needs a synchronous wait. If that is unavoidable, block on DownloadStringTaskAsync, not on DownloadStringAsync itself.
This still blocks the current thread, so it is not ideal for UI code, but it is at least straightforward.
Avoid Blocking on UI Threads
If you call .Result, .Wait(), or .GetAwaiter().GetResult() on a UI thread, you risk deadlocks or frozen interfaces depending on the environment and synchronization context.
That is why desktop and mobile UI code should prefer await all the way.
Good pattern:
HttpClient Is the Modern Alternative
WebClient is legacy API. In modern .NET code, HttpClient is the preferred choice.
This is the approach most new code should use unless you are maintaining an older codebase that already depends on WebClient.
Converting an Event to a Task Manually
If you absolutely have to work with the event API and cannot switch, you can bridge it into a TaskCompletionSource.
This is useful mainly when wrapping legacy APIs inside a modern async interface.
Common Pitfalls
- Expecting
DownloadStringAsyncitself to be awaitable when it is event-based. - Blocking on async work from a UI thread and freezing the application.
- Keeping new code on
WebClientwhenHttpClientis a better fit. - Mixing event-based async and task-based async without a clear bridge.
- Forgetting to handle errors and cancellation in the completion callback.
Summary
- '
DownloadStringAsyncuses the old event-based async pattern, so it does not return aTask.' - If you want to wait cleanly, use
DownloadStringTaskAsyncinstead. - In modern .NET, prefer
HttpClientwithawait. - Blocking is possible, but it should be avoided on UI threads.
- If needed, wrap the event-based API in a
TaskCompletionSourcefor cleaner async composition.
Related reading
- Duplicate ID, tag null, or parent id with another fragment for com.google.android.gms.maps.MapFragment
- DynamoDB API How can I build an add JSON attribute if not present update request?
- Easiest way to post to a server on iPhone? I don't care about the response
- EC2 instance has no public DNS
- Dynamic periodic tasks - alternatives to Celery beat
- Dynamically creating asynchronous message queues in Java
- Drag and drop files into WPF
- Draw a single pixel on Windows Forms

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.