Подтвердить что ты не робот

Превратите метод синхронизации в асинхронный

Я пытаюсь превратить синхронный метод из какого-то старого кода в асинхронный метод, но у меня возникли проблемы с пониманием. Из всех видео и учебных пособий, которые я прочитал, они, кажется, создают два метода: один - настоящую функцию, другой - обертку, а затем обертку, вызываемую в пользовательском интерфейсе.

Вот мой код:

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

По какой-то причине, когда я пытаюсь вызвать метод в потоке пользовательского интерфейса от нажатия кнопки, он просит меня сделать событие кнопки async.

txtLog.AppendText("Before Await");

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

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

Нужен ли мне метод-обертка, который также установлен в async который делает вызов для входа в систему?

4b9b3361

Ответ 1

Чтобы ответить на вашу вторую часть, да, вам нужно отметить событие для кнопки 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 witch часть IRestClient. Эта функция вызывает метод обратного вызова, когда он завершается, в методе обратного вызова я вызываю tcs SetResult, чтобы сообщить результат, который я хотел.

Вы можете расширить это, взяв 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;
    }
}

Ответ 2

Обработчик вашей кнопки использует ключевое слово await, которое требует, чтобы оно было сделано async. Ключевое слово await в основном разделяет метод на await, превращая часть после await в делегат, который продолжается, когда завершается ожидаемый Task. Метод возвращается сразу после встречи с await.

Ваша функция входа не использует await. Для этого не требуется ключевое слово async.