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

Jam*_*ery 10 c# async-await

我正在尝试将同步方法从一些旧代码转换为异步方法,但我有一些麻烦.从我读过的所有视频和教程中,他们似乎都在创建方法,一个是实际的函数,另一个是包装器,然后是在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"); 
}
Run Code Online (Sandbox Code Playgroud)

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

txtLog.AppendText("Before Await");

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

txtLog.AppendText("After Await");
txtLog.AppendText("Result: " + result.toString());
Run Code Online (Sandbox Code Playgroud)

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

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

Sco*_*ain 13

要先回答第二部分,是的,您需要为按钮标记事件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());
}
Run Code Online (Sandbox Code Playgroud)

重写函数方法:

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;
}
Run Code Online (Sandbox Code Playgroud)

在我的"重写方法"中,我正在做的是我使用的ExecuteAsync女巫是IRestClient的一部分.当它完成,在回调方法我调用该函数调用回调方法tcsSetResult报到我想要的结果.

你可以采取的进一步扩大这个CancellationToken,如果令牌提高通话Abort()RestRequestAsyncHandle,但是如果我们做到这一点,我们需要把async给函数背部和等待的结果,所以我们可以在取消标记注册后清理.

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;
    }
}
Run Code Online (Sandbox Code Playgroud)