我正在尝试将一些旧代码的同步方法转换为异步方法,但我在理解时遇到了一些麻烦。从我读过的所有视频和教程来看,他们似乎正在创建两种方法:一种是实际功能,另一种是包装器,然后是在 UI 上调用的包装器。
这是我的代码:
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");
}
出于某种原因,当我尝试通过单击按钮调用 UI 线程上的方法时,它要求我将按钮事件设置为async
。
txtLog.AppendText("Before Await");
Task<bool> result = await login("","");
txtLog.AppendText("After Await");
txtLog.AppendText("Result: " + result.toString());
我是否需要一个也设置为 async
的包装器方法来调用登录?
最佳答案
首先回答你的第二部分,是的,你需要为按钮异步
标记事件,如果你想在你的代码中使用关键字await
,你必须声明函数 async
。
第二,如果一个函数使用 async
而没有 await
代码将不会异步运行,你要么需要创建一个任务并运行你的同步方法在其中或将方法重写为异步。
作为任务方法:
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());
}
重写函数方法:
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;
}
在我的“重写方法”中,我正在使用 ExecuteAsync
女巫是 part of IRestClient .该函数在完成时调用回调方法,在回调方法中我调用了 tcs
的 SetResult
来报告我想要的结果。
您可以通过接收 CancellationToken
来进一步扩展它,如果 token 被引发,则调用 RestRequestAsyncHandle
上的 Abort()
,但是如果我们为此,我们需要将 async
带回函数并等待结果,以便我们可以在取消 token 注册后进行清理。
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;
}
}
关于c# - 将同步方法变成异步方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18166486/