Calling a C async WebMethod using jQuery's .ajax hangs endlessly
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
When a C# async WebMethod is called from jQuery's $.ajax() and the request hangs indefinitely, the cause is almost always a deadlock caused by .Result or .Wait() on an async call within the ASP.NET synchronization context. ASP.NET WebForms uses a single-threaded SynchronizationContext that requires the request thread to process continuations. If that thread is blocked waiting for the async result, the continuation can never run, creating a classic deadlock. The fix is to use async/await all the way through or use ConfigureAwait(false) on internal async calls.
The Deadlock Pattern
The ASP.NET SynchronizationContext marshals continuations back to the original request thread. When .Result blocks that thread, the await continuation cannot execute, and both are stuck waiting for each other.
The Fix: Async All the Way
Making the WebMethod itself async Task<string> allows ASP.NET to release the request thread during await, preventing the deadlock. The response is sent when the Task completes.
The jQuery Side
The jQuery side is rarely the problem — the hang originates on the server. The AJAX request simply waits until the server responds (which it never does when deadlocked).
Fix with ConfigureAwait(false)
ConfigureAwait(false) prevents the continuation from trying to marshal back to the ASP.NET thread. This avoids the deadlock but is a workaround — making the WebMethod fully async is the proper solution.
Passing Parameters from AJAX
Parameter names in the JSON payload must match the WebMethod parameter names exactly (case-sensitive in some configurations).
Diagnosing the Hang
Common Pitfalls
- Calling
.Resultor.Wait()on async code in ASP.NET: This is the primary cause of the deadlock. The ASP.NET synchronization context requires the request thread for continuations, but.Resultblocks that thread. Always useasync/awaitfrom the entry point (the WebMethod) down through all async calls. - Forgetting to set
contentTypetoapplication/json: ASP.NET WebMethods requirecontentType: "application/json; charset=utf-8"in the AJAX call. Without it, the server returns a 500 error or the method is not invoked at all, which looks like a hang if no error handler is attached. - Not making the WebMethod
public static: ASP.NET page methods must bepublic staticand decorated with[WebMethod]. An instance method or missing attribute silently fails — the AJAX call receives no response. - Using
ConfigureAwait(false)as a blanket fix: WhileConfigureAwait(false)breaks the deadlock, it means the continuation runs on a thread pool thread without the ASP.NET context. If downstream code accessesHttpContext.Current, it will be null. Only use it in library code that does not need the HTTP context. - Missing error handling on the jQuery side: Without an
errorcallback in$.ajax(), failed requests silently disappear. Always add error handling to distinguish between a server deadlock (timeout), a 500 error, and a network failure.
Summary
- The deadlock occurs when
.Resultor.Wait()blocks the ASP.NET request thread that async continuations need - Fix by making the WebMethod
async Task<string>and usingawaitthroughout ConfigureAwait(false)is a workaround for library code but losesHttpContext- The jQuery AJAX call itself is rarely the cause — the hang is server-side
- Always set
contentType: "application/json"and add error handlers to$.ajax() - Use
async/awaitall the way from the WebMethod to the lowest async call

