Bastien Vandamme
Bastien Vandamme

Reputation: 18485

Understanding async await instruction during a HttpClient request

Let's say I have a solution composed of 2 projects. An old WinForm classic project. In this old project, I have a login window. On click "OK" of this login window, I start an event that will call a REST API. Both applications start at the same time in debug mode.

Somewhere in my code, I have this code:

public async Task<User> Login(string username, string password)
{
    HttpResponseMessage response = await Client.GetAsync($"api/Login?login={username}&password={password}");
    if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
        return new User();
    response.EnsureSuccessStatusCode();
    var userDto = await response.Content.ReadAsAsync<UserDto>();
    var user = userDto.ToUser();
    return user;
}

On the first line when I call the Client.GetAsync I call my API. In my API I properly receive the call and I properly return an Ok with my User object or I return another code. It works. My API works. But then nothing. My client never continues. It seems Client.GetSync waits for something. I never go on the next step where I evaluate the StatusCode.

public async Task<User> Login(string username, string password)
{
    HttpResponseMessage response = Client.GetAsync($"api/Login?login={username}&password={password}").Result;
    if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
        return new User();
    response.EnsureSuccessStatusCode();
    var userDto = await response.Content.ReadAsAsync<UserDto>();
    var user = userDto.ToUser();
    return user;
}

Same code without the await I have no problem. My code run till the next step. Proof my API is not the problem.

This is clear it is an issue related to await/async. I must do something wrong but what? Can you help me? Is it related to the debugger?

For more information here is a picture of my code before

enter image description here

And after I click for the next step. Note my call stack is empty and code is still running.

enter image description here

As requested here is the code where I call the login. I just added the Async word before the Sub and changed the _authService.Login(username, password).Result by await _authService.Login(username, password)

I works now.

Private Async Sub ButLogin_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles butLogin.Click
    DataProxies.SetToken()
    Dim _authService As IAuthenticationService = New AuthenticationService()


    Dim username As String = txtLogin.Text
    Dim password As SecureString = New NetworkCredential(String.Empty, txtPwd.Text).SecurePassword

    Dim auth As Tuple(Of Boolean, User) = Await _authService.Login(username, password)
    If (auth.Item1) Then
        Dim user As User = auth.Item2
        Name = $"{user.FirstName} {user.LastName}"
        ApiInformations.ApiToken = user.SessionToken
    End If
End Sub

Upvotes: 2

Views: 589

Answers (1)

Stephen Cleary
Stephen Cleary

Reputation: 456377

I just added the Async word before the Sub and changed the _authService.Login(username, password).Result by await _authService.Login(username, password)

The general guidance is "Don't block on async code". This is one of the asynchronous programming best practices.

Blocking on asynchronous code is bad because await works by capturing a "context" by default, and resumes executing the async method in that context. One context is the UI context, which causes the async method to resume executing on the UI thread.

So the deadlock you were seeing was caused by blocking the UI thread. The code was calling the async method and then blocking the UI thread until that async method completed. However, the await in that async method captured the UI context, so it was waiting for the UI thread to be free before it could complete. The UI thread was waiting for the async method and the async method was waiting for the UI thread: deadlock.

The reason your fix worked is that the UI thread is no longer blocked waiting for the async method, so there's no more deadlock.

Upvotes: 5

Related Questions