2013-08-10 16 views
11

Tôi đang cố chuyển một phương thức đồng bộ từ một số mã cũ thành phương pháp không đồng bộ, nhưng tôi ' m có một số khó hiểu. Từ tất cả các video và hướng dẫn tôi đã đọc họ dường như được tạo ra để phương pháp, một trong những chức năng thực tế, một wrapper khác và sau đó nó là wrapper được gọi là trên giao diện người dùng.Tôi đang cố gắng chuyển một phương thức đồng bộ từ một số mã cũ thành một phương pháp không đồng bộ, nhưng tôi gặp một số sự cố hiểu được

Đây là mã của tôi:

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"); 
} 

Đối với một số lý do khi tôi cố gắng gọi phương thức trên thread UI từ một nút bấm, nó hỏi tôi để làm cho async nút sự kiện.

txtLog.AppendText("Before Await"); 

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

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

Tôi có cần một phương thức bao bọc cũng được đặt thành không đồng bộ làm cho cuộc gọi đăng nhập không?

Tất cả điều này có vẻ hơi phức tạp.

Trả lời

13

Để trả lời phần thứ hai trước, bạn cần đánh dấu sự kiện cho nút async, nếu bạn muốn sử dụng từ khóa await trong mã của mình, bạn phải khai báo hàm async.

Thứ hai nếu hàm sử dụng async mà không cần await bên trong mã sẽ không chạy không đồng bộ, bạn cần phải tạo công việc và chạy phương thức đồng bộ bên trong hoặc viết lại phương thức không đồng bộ.

Là phương pháp nhiệm vụ:

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()); 
} 

Viết lại phương pháp chức năng:

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; 
} 

Trong "Phương pháp viết lại" của tôi những gì tôi đang làm là tôi đang sử dụng ExecuteAsync phù thủy là part of IRestClient. Hàm đó gọi phương thức gọi lại khi nó hoàn thành, trong phương thức gọi lại, tôi gọi's SetResult để báo cáo lại kết quả mà tôi muốn.

Bạn có thể mở rộng này hơn nữa bằng cách tham gia trong một CancellationToken và nếu token được nâng lên gọi Abort() trên RestRequestAsyncHandle, tuy nhiên nếu chúng ta làm được điều này chúng ta cần phải đưa async lại vào chức năng và chờ đợi kết quả vì vậy chúng tôi có thể dọn dẹp sau khi đăng ký mã thông báo hủy.

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 bạn vừa trả lời hai câu hỏi trong một cho tôi vì tôi chỉ tìm cách sử dụng ExecuteAsync và đang bối rối về cách sử dụng nó. Ngoài ra, tôi đã sử dụng CsQuery có phương pháp "HasClass". –

+0

Tôi đã thực hiện một vài chỉnh sửa trên phiên bản có thể hủy, bạn cần phải thông báo 'tcs' rằng công việc đã bị hủy.Tôi cũng thêm hỗ trợ cho nó báo cáo bất kỳ trường hợp ngoại lệ có thể được ném vào cuộc gọi lại. –

+0

Câu trả lời này hữu ích cho tôi. Cảm ơn bạn. –

1

Trình xử lý nút của bạn sử dụng từ khóa await, yêu cầu phải được thực hiện async. Từ khóa await về cơ bản phân vùng phương thức ở số await, chuyển phần sau await thành đại biểu tiếp tục khi chờ đợi Task hoàn tất. Phương thức trả về ngay lập tức sau khi gặp phải await.

Chức năng đăng nhập của bạn không sử dụng await. Nó không cần từ khóa async.

Các vấn đề liên quan