Хотите прочитать HTML-разметку веб-страницы. Что я здесь делаю не так?

namespace WebReader
{
    public partial class frm_Main : Form
    {
        public frm_Main()
        {
            InitializeComponent();
        }

        private void frm_Main_Load(object sender, EventArgs e)
        {
            using var clt= new HttpClient();
            var res = clt.GetAsync("https://daera.net/dmoon/testfile.txt");

            while (true)
            {
                if (res.IsCompletedSuccessfully)
                {
                    var msg = clt.GetStringAsync("https://daera.net/dmoon/testfile.txt");
                    MessageBox.Show(msg.ToString());
                    break;
                }
            }
        }
    }
}

Предполагается, что этот код отображает содержимое текстового файла https://daera.net/dmoon/testfile.txt, но он показывает окно сообщения с некоторой технической информацией об объекте clt и ничего больше.

Что я делаю неправильно? Я использую Visual Studio 2019.

Вы не ожидаете звонков, поэтому объекты, которые вы используете, просто Task<T> и пока не содержат ничего действенного.

maccettura 20.08.2024 21:29

Вам также нужен только звонок httpClient.GetStringAsync(), тот, который вы сделали раньше, является избыточным.

maccettura 20.08.2024 21:38

Вызов GetStringAsync() также является асинхронным и возвращает Task<String>. Вы могли бы сделать с ним что-то похожее на то, что вы сделали с возвращением GetAsync(). В любом случае, я бы заменил цикл while (true) вызовом Task.Wait(). Также см. метод ContinueWith().

Diego Ferruchelli 20.08.2024 21:52
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
77
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Упрощаем, но вам нужны await результаты асинхронных методов:

using var clt= new HttpClient();
var res = await clt.GetAsync("https://daera.net/dmoon/testfile.txt");

Сделайте это, и петля while (true) вам не понадобится.

Хорошо, но поскольку frm_Main_Load, в котором находится этот код, не отмечен async, то await будет невозможно - нет?

marc_s 20.08.2024 22:10

Это верно. Ключевое слово async «позволяет использовать ожидание». В любом случае, ваша текущая проблема не в том, как вы синхронизируете; дело в том, что вы не синхронизируете вторую задачу. Проверьте мой ответ.

Diego Ferruchelli 20.08.2024 22:18

В результате появляется сообщение об ошибке: CS4033 Оператор ожидания можно использовать только внутри асинхронного метода. Рассмотрите возможность пометки этого метода модификатором async и изменения типа возвращаемого значения на Task.

Youstay Igo 21.08.2024 15:08

@YoustayIgo, так сделай это: отметьте метод «async».

Joel Coehoorn 21.08.2024 15:53

@JoelCoehoorn Как это сделать? Я новичок в C#. Я знаю VB6 и C++, но я совсем новичок в Visual Studio и C#. Извините, что я такой зеленый.

Youstay Igo 21.08.2024 16:04
Ответ принят как подходящий

Как я уже говорил в комментариях, вызов GetStringAsync() также асинхронен и возвращает Task<String>.

У вас есть несколько вариантов синхронизации этих задач с вашей основной программой.

  1. Тот, который вы использовали: цикл while(true), проверяющий IsCompletedSuccessfully. Это вообще не рекомендуется, потому что это съедает ваши ресурсы, ничего не делая.
  2. Используя Task.Wait(). Это блокирующий вызов и то, что вы ищете. (Отредактировано: он ближе к исходному циклу без потребления ресурсов; но вы, вероятно, не хотите блокировать свою форму до тех пор, пока не завершится HTTP-вызов, что приведет к тому, что ваша программа перестанет отвечать на запросы. Найдите вариант 3 или Task.ContinueWith())
  3. Использование await, как в ответе @Joel (*).

Это ваш код, использующий вариант 1 для GetAsync() (опять же, не рекомендуется) и вариант 2 для GetStringAsync().

namespace WebReader
{
    public partial class Program
    {
        static void Main ()
        {
            using var clt= new HttpClient();
            var res = clt.GetAsync("https://daera.net/dmoon/testfile.txt");
            while (true)
            {
                if (res.IsCompletedSuccessfully)
                {
                    var responseTask = clt.GetStringAsync("https://daera.net/dmoon/testfile.txt");
                    responseTask.Wait();
                    Console.WriteLine(responseTask.Result.ToString());
                    break;
                }
                
            }
        }
    }
}

(*) Более технический. Асинхронные функции выполняются в отдельной задаче (представьте это как отдельный поток или облегченный процесс, но несколько задач могут фактически повторно использовать один и тот же поток). Когда вы используете await для вызова асинхронной функции B из функции A, вызывающая сторона (A) не будет выполнять инструкции, следующие за вызовом, пока асинхронная задача (и вызываемая сторона B) не завершится; но A немедленно вернется к вызывающему абоненту. То есть вызывающая сторона (A) также будет асинхронной (и это причина обязательного ключевого слова async). Если A — ваша функция main(), она может вернуться в среду выполнения .NET (цикл Windows Forms и т. д.), пока асинхронные задачи все еще выполняются.

Отредактировано. Как уже было сказано, использовать await проще, если вы можете пометить свою функцию Main() как async. Я не знаю, сможете ли вы сделать это с вашей версией Windows Forms (читайте комментарий @Mixin), но вы можете взять этот код консольного приложения (который работает) в качестве примера:

namespace WebReader
{
    public partial class Program
    {
        public static async Task Main ()
        {
            using var clt = new HttpClient();
            var response = await clt.GetAsync("https://daera.net/dmoon/testfile.txt");
            // You should check response.StatusCode here
            var responseContent = await clt.GetStringAsync("https://daera.net/dmoon/testfile.txt");
            Console.WriteLine(responseContent.ToString());
        }
    }
}

Как вариант, вы можете написать точно такой же код в отдельной функции, как эта, и вызывать эту функцию из фактического frm_Main(), используя Task.Wait(yourAsyncFunction()).

Это работает, да. Но как сделать это эффективно с помощью async и await?

Youstay Igo 21.08.2024 16:13

@YoustayIgo Используйте private async void frm_Main_Load(object sender, EventArgs e)

Minxin Yu - MSFT 21.08.2024 16:24

Я отредактировал свой ответ, включив в него пример техники await. Это по-прежнему консольное приложение, но суть вы поняли.

Diego Ferruchelli 21.08.2024 17:50

@MinxinYu-MSFT Это работает. Спасибо, что покормили новичка с ложечки. Весьма признателен. Отметил этот ответ как принятый.

Youstay Igo 21.08.2024 18:41

Другие вопросы по теме