我正在尝试将同步方法从一些旧代码转换为异步方法,但我有一些麻烦

我正在尝试将同步方法从一些旧代码转换为异步方法,但我有一些麻烦。 从我读过的所有video和教程中,他们似乎都在创建方法,一个是实际的函数,另一个是包装器,然后是在UI上调用的包装器。

这是我的代码:

private async Task login(String username, String password) { var tcs = new TaskCompletionSource(); 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"); } 

出于某种原因,当我尝试从按钮单击调用UI线程上的方法时,它要求我使按钮事件异步。

 txtLog.AppendText("Before Await"); Task result = await login("",""); txtLog.AppendText("After Await"); txtLog.AppendText("Result: " + result.toString()); 

我是否需要一个也设置为async的包装器方法来调用登录?

所有这些看起来有点复杂。

要先回答第二部分,是的,您需要为按钮async标记事件,如果要在代码中使用关键字await ,则必须声明函数async

2,如果一个函数使用async而没有在其中进行await ,则代码将不会异步运行,您需要创建一个任务并在其中运行同步方法或将该方法重写为异步。

作为任务方法:

 private async void button1_Click(object sender, EventArgs e) { txtLog.AppendText("Before Await"); //Note I changed from "Task" 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()); } 

重写函数方法:

 private async void button1_Click(object sender, EventArgs e) { txtLog.AppendText("Before Await"); //Note I changed from "Task" 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 login(String username, String password) { var tcs = new TaskCompletionSource(); // 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; } 

在我的“重写方法”中,我正在使用的是ExecuteAsync是IRestClient的一部分 。 该函数在完成时调用一个回调方法,在回调方法中我调用tcsSetResult来报告我想要的结果。

您可以通过接受CancellationToken进一步扩展这一点,如果引发了令牌,则在RestRequestAsyncHandle上调用Abort() ,但是如果我们这样做,我们需要将async带回函数并等待结果,以便我们可以在取消令牌注册。

 private Task login(String username, String password) { return login(username, password, CancellationToken.None); } private async Task login(String username, String password, CancellationToken cancelToken) { var tcs = new TaskCompletionSource(); // 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; } } 

您的按钮处理程序使用await关键字,这需要将其设置为asyncawait关键字基本上在await处对方法进行分区,将await之后的部分转换为在等待的Task完成时继续的委托。 该方法在遇到await后立即返回。

您的登录function不使用await 。 它不需要async关键字。