HttpClient.GetStringAsync 未执行

Cyc*_*eak 9 c# async-await dotnet-httpclient .net-standard-2.0

代码在 .NET Standard 2.0 中运行。我有一个构造函数调用一个方法,该方法将调用这样的 Azure 函数:

public ChatViewModel(IChatService chatService)
{
    Task.Run(async () =>
    {
        if (!chatService.IsConnected)
        {
            await chatService.CreateConnection();
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

方法是这样的:

public async Task CreateConnection()
{
   await semaphoreSlim.WaitAsync();

   if (httpClient == null)
   {
        httpClient = new HttpClient();
   }

   var result = await httpClient.GetStringAsync(uri);

   var info = JsonConvert.DeserializeObject<Models.ConnectionInfo>(result);

   //... some other code ...
   semaphoreSlim.Release();
}
Run Code Online (Sandbox Code Playgroud)

代码停在

等待 httpClient.GetStringAsync(uri)

URI 是 100% 有效的,如果我将它复制并粘贴到浏览器中,我会得到我想要的 JSON。

打开 Fiddler 时,没有调用 URI。

编辑 源代码来自这个 Github 存储库:https : //github.com/PacktPublishing/Xamarin.Forms-Projects/tree/master/Chapter06-07/Chat/Chat

奇怪的是,我似乎在 Azure 中接到了一个电话: 在此处输入图片说明

编辑 2 此代码正在运行:

static void Main(string[] args)
{
    using (var httpClient = new HttpClient())
    {
        var a = httpClient.GetStringAsync(uri).GetAwaiter().GetResult();
    }
}
Run Code Online (Sandbox Code Playgroud)

此代码不起作用:

static void Main(string[] args)
{
    Task.Run(async() => {
        using (var httpClient = new HttpClient())
        {
            var a = await httpClient.GetStringAsync(uri);
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

Arm*_*our 12

我猜您的问题是因为您可能在控制台应用程序(或 Windows 服务)中运行它而没有任何代码片段来保持您的应用程序活动。

此代码不起作用:

static void Main(string[] args)
{
    Task.Run(async() => {
        using (var httpClient = new HttpClient())
        {
            var a = await httpClient.GetStringAsync(uri);
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

这段代码显然不起作用,因为Task.Run创建了一个后台线程和Main方法,然后立即完成(并且分别导致您的任务终止)。只需Console.ReadKey()Main方法末尾添加以使其保持活动状态,您将看到一切正常:

static void Main(string[] args)
{
    Task.Run(async() => {
        using (var httpClient = new HttpClient())
        {
            var a = await httpClient.GetStringAsync(uri);
        }
    });

    Console.ReadKey();
}
Run Code Online (Sandbox Code Playgroud)

另一种选择是为MainC# 8 中引入的方法使用新的异步入口点:

static async Task Main(string[] args)
{
    using (var httpClient = new HttpClient())
    {
        var a = await httpClient.GetStringAsync(uri);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我想补充一点,您可能想要等待创建的后台任务。最合乎逻辑的意图似乎是等到请求完成,而不是等到用户(如果确实有)任意按下某个键。 (2认同)

Seb*_*ian 6

这可能是在构造函数中调用异步代码的问题。.GetAwaiter().GetResult()如果您真的想在那时运行异步代码,您应该使用:

public ChatViewModel(IChatService chatService)
{
    Task.Run(async () =>
    {
        if (!chatService.IsConnected)
        {
            await chatService.CreateConnection();
        }
    }).GetAwaiter().GetResult();
}
Run Code Online (Sandbox Code Playgroud)

我更喜欢一个单独的异步 Init 方法,该方法将在创建实例后调用。另一种可能的解决方案是创建一个异步 GetInfo() 方法:

private async Task<Models.ConnectionInfo> GetInfo(){
    if(_info != null)
        return _info;

    await semaphoreSlim.WaitAsync();
    try{
        if(_info != null)
            return _info;

        if (httpClient == null)
        {
            httpClient = new HttpClient();
        }

        var result = await httpClient.GetStringAsync(uri);

        var info = JsonConvert.DeserializeObject<Models.ConnectionInfo>(result);

        //... some other code ...

        return _info = info;
    } finally {
        semaphoreSlim.Release();
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以调用该方法来获取您的信息,并且代码只会在第一次使用时运行。