2013-08-10 13 views
11

Próbuję przekształcić metodę synchroniczną ze starego kodu w metodę asynchroniczną, ale ja ' Mam problemy z rozumieniem. Ze wszystkich filmów i samouczków, które przeczytałem, wydają się tworzyć metody, jedna rzeczywista, druga to opakowanie, a następnie to opakowanie w interfejsie użytkownika.Próbuję zmienić metodę synchroniczną z jakiegoś starego kodu w metodę asynchroniczną, ale mam problemy z zrozumieniem

Oto mój kod:

private async Task<bool> login(String username, String password) 
{ 
     var tcs = new TaskCompletionSource<RestSharp.IRestResponse>(); 

     RestSharp.RestRequest request = new RestSharp.RestRequest("/accounts/login/", RestSharp.Method.GET); 
     RestSharp.IRestResponse response = client.Execute(request); 

     // Make the login request 
     request = new RestSharp.RestRequest("/accounts/login/", RestSharp.Method.POST); 
     request.AddParameter("username", username); 
     request.AddParameter("password", password); 

     response = client.Execute(request); 

     // Return loggin status 
     dom = response.Content; 
     return dom["html"].HasClass("logged-in"); 
} 

Z jakiegoś powodu, gdy próbuję wywołać metodę w wątku UI od kliknięcia przycisku, to mnie z prośbą, aby asynchronicznie zdarzenia przycisk.

txtLog.AppendText("Before Await"); 

Task<bool> result = await login("",""); 

txtLog.AppendText("After Await"); 
txtLog.AppendText("Result: " + result.toString()); 

Czy muszę metodę otoki, który jest także zestaw do async co sprawia, że ​​rozmowa się zalogować?

Wszystko to wydaje się nieco skomplikowane.

Odpowiedz

13

Aby odpowiedzieć na to 2nd część pierwsza, tak trzeba zaznaczyć zdarzenia dla przycisku async, jeśli chcesz użyć słowa kluczowego await w kodzie trzeba zadeklarować funkcję async.

2ndly jeśli funkcja wykorzystuje async bez posiadania await wewnątrz niego kod nie będzie uruchamiane asynchronicznie, albo trzeba utworzyć zadanie i uruchomić synchroniczne metodę wewnątrz niego lub przepisać metodę być asynchroniczny.

jako metoda zadanie:

private async void button1_Click(object sender, EventArgs e) 
{ 
    txtLog.AppendText("Before Await"); 

    //Note I changed from "Task<bool>" to "bool", await is like calling ".Result" 
    // on a task but not blocking the UI, so you store the type you are waiting for. 
    bool result = await Task.Run(() => login("","")); //You would still use your old login code before you tried to make it async, it requires no modifications. 

    txtLog.AppendText("After Await"); 
    txtLog.AppendText("Result: " + result.ToString()); 
} 

Przepisanie sposobu funkcję:

private async void button1_Click(object sender, EventArgs e) 
{ 
    txtLog.AppendText("Before Await"); 

    //Note I changed from "Task<bool>" to "bool", await is like calling ".Result" 
    // on a task but not blocking the UI, so you store the type you are waiting for. 
    bool result = await login("",""); 

    txtLog.AppendText("After Await"); 
    txtLog.AppendText("Result: " + result.ToString()); 
} 

private Task<bool> login(String username, String password) 
{ 
    var tcs = new TaskCompletionSource<bool>(); 

    // Make the login request 
    var request = new RestSharp.RestRequest("/accounts/login/", RestSharp.Method.POST); 
    request.AddParameter("username", username); 
    request.AddParameter("password", password); 

    client.ExecuteAsync(request, (response, handle) => 
     { 
      try 
      { 
       // Return loggin status 
       var dom = response.Content; 

       //dom["html"] did not have a .HasClass in my tests, so this code may need work. 
       tcs.TrySetResult(dom["html"].HasClass("logged-in")); 
      } 
      catch(Exception ex) 
      { 
       tcs.TrySetException(ex); 
      } 
     }); 

    return tcs.Task; 
} 

W moim "metody przepisywania" co robie to używam ExecuteAsync czarownica jest part of IRestClient. Ta funkcja wywołuje metodę wywołania zwrotnego po jej zakończeniu w metodzie wywołania zwrotnego, którą wywołuję tcs 's, aby otrzymać wynik, który chciałem.

Można rozszerzyć ten dodatkowo biorąc w CancellationToken a jeśli znacznik jest podniesiona połączenia Abort() na RestRequestAsyncHandle, jednak jeśli to zrobimy musimy wnieść async powrotem do funkcji i poczekać na wynik więc możemy oczyścić po rejestracji tokena anulowania.

private Task<bool> login(String username, String password) 
{ 
    return login(username, password, CancellationToken.None); 
} 

private async Task<bool> login(String username, String password, CancellationToken cancelToken) 
{ 
    var tcs = new TaskCompletionSource<bool>(); 

    // Make the login request 
    var request = new RestSharp.RestRequest("/accounts/login/", RestSharp.Method.POST); 
    request.AddParameter("username", username); 
    request.AddParameter("password", password); 

    var asyncHandle = client.ExecuteAsync(request, (response, handle) => 
     { 
      try 
      { 
       // Return loggin status 
       var dom = response.Content; 

       tcs.TrySetResult(dom["html"].HasClass("logged-in")); 
      } 
      catch(Exception ex) 
      { 
       tcs.TrySetException(ex); 
      } 
     }); 

    //if the token is canceled it will call `asyncHandle.Abort()` for us. 
    using(cancelToken.Register(() => 
     { 
      if(tcs.TrySetCanceled(cancelToken)) 
       asyncHandle.Abort(); 
     })) 
    { 
     return await tcs.Task; 
    } 
} 
+0

Scott właśnie odpowiedział na dwa pytania w jednym dla mnie, bo ja po prostu patrząc w górę, jak korzystać ExecuteAsync i był już wiedzą jak z niego korzystać. Ponadto używałem CsQuery, który ma metodę "HasClass". –

+0

Wprowadziłem kilka poprawek w wersji podlegającej anulowaniu, musisz powiadomić 'tcs', że zadanie zostało anulowane.Dodałem także obsługę tego zgłoszenia wszelkich wyjątków, które mogą być zgłaszane na wywołanie zwrotne. –

+0

Ta odpowiedź jest dla mnie pomocna. Dziękuję Ci. –

1

Twoja funkcja obsługi przycisku używa słowa kluczowego await, które wymaga, aby zostało utworzone jako async. Słowo kluczowe await zasadniczo dzieli metodę na await, zamieniając część po await w delegata, która jest kontynuowana po zakończeniu oczekiwanego Task. Metoda zwraca się natychmiast po napotkaniu numeru await.

Twoja funkcja logowania nie używa await. Nie wymaga słowa kluczowego async.

Powiązane problemy