异步 REST API 从不调用 returns,但其他异步操作按预期工作
Async REST API call never returns, but other async operations work as expected
对于我的确切问题,我的搜索并不是很有成果;我似乎无法让异步 REST 调用在我的 Xamarin Forms 应用程序中正常工作。举个例子:
public async Task<GPIOFunctions> GetGPIOFunction(int gpioNumber)
{
var response = await _client.GetAsync(GetFullUrl($"/GPIO/{gpioNumber}/function"));
response.EnsureSuccessStatusCode();
return GetFunctionFromString(await response.Content.ReadAsStringAsync());
}
我的单元测试调用这个函数,并且总是工作正常。但是,当我的应用程序调用它时,它从来没有 returns 从第一次等待开始。如果我向我的 GetAsync
添加一个 .GetAwaiter().GetResult()
,那么它 returns,甚至正确地执行最后一个 ReadAsStringAsync()
,returns 到调用者,一切继续.
现在,如果我在末尾添加一个 .ConfigureAwait(false)
,我可以让我的 GetAsync()
工作,但是第二个 await 永远不会完成,并且添加 .ConfigureAwait(false)
并没有纠正问题。
我启用了对所有异常的中断,查看了调试输出,但都没有提供任何其他信息。此外,我已经使用 Fiddler 运行 调试了该应用程序,但从未看到发出请求,因此我认为这不是我的 emulator/device 无法访问 API 的问题(是的,我的清单中启用了 INTERNET
权限。
这是 Xamarin Forms 的已知限制吗?
编辑:
下面的函数调用层次结构,从我的视图模型的构造函数开始:
public MainViewModel()
{
_client = new HttpEndpoint(Url, User, Pass);
TriggerDoorCommand = new Command(async () => await ExecuteTriggerDoorCommand());
_currentDoorFunc = GetCurrentDoorFunction().Result;
}
private async Task<GPIOFunctions> GetCurrentDoorFunction()
{
return await _client.GetGPIOFunction(DOOR_TOGGLE_PIN);
}
附加信息,class HttpEndpoint
位于单独的 class 库中,并包装了 HttpClient
和所有 API 调用。共享项目和这个 class 库都是 .NET Standard 2.0。
不要将 async/await
与 .Result
等阻塞调用混合使用,这可能会导致死锁。
您可以创建事件和事件处理程序作为解决方法
public MainViewModel() {
_client = new HttpEndpoint(Url, User, Pass);
TriggerDoorCommand = new Command(async () => await ExecuteTriggerDoorCommand());
//Subscribe to event
GetData += GetDataHandler;
//Raise event
GetData(this, EventArgs.Empty);
}
private event EventHandler GetData = delegate { };
private async void GetDataHandler(object sender, EventArgs args) {
_currentDoorFunc = await GetCurrentDoorFunction();
}
private async Task<GPIOFunctions> GetCurrentDoorFunction() {
return await _client.GetGPIOFunction(DOOR_TOGGLE_PIN);
}
对于我的确切问题,我的搜索并不是很有成果;我似乎无法让异步 REST 调用在我的 Xamarin Forms 应用程序中正常工作。举个例子:
public async Task<GPIOFunctions> GetGPIOFunction(int gpioNumber)
{
var response = await _client.GetAsync(GetFullUrl($"/GPIO/{gpioNumber}/function"));
response.EnsureSuccessStatusCode();
return GetFunctionFromString(await response.Content.ReadAsStringAsync());
}
我的单元测试调用这个函数,并且总是工作正常。但是,当我的应用程序调用它时,它从来没有 returns 从第一次等待开始。如果我向我的 GetAsync
添加一个 .GetAwaiter().GetResult()
,那么它 returns,甚至正确地执行最后一个 ReadAsStringAsync()
,returns 到调用者,一切继续.
现在,如果我在末尾添加一个 .ConfigureAwait(false)
,我可以让我的 GetAsync()
工作,但是第二个 await 永远不会完成,并且添加 .ConfigureAwait(false)
并没有纠正问题。
我启用了对所有异常的中断,查看了调试输出,但都没有提供任何其他信息。此外,我已经使用 Fiddler 运行 调试了该应用程序,但从未看到发出请求,因此我认为这不是我的 emulator/device 无法访问 API 的问题(是的,我的清单中启用了 INTERNET
权限。
这是 Xamarin Forms 的已知限制吗?
编辑:
下面的函数调用层次结构,从我的视图模型的构造函数开始:
public MainViewModel()
{
_client = new HttpEndpoint(Url, User, Pass);
TriggerDoorCommand = new Command(async () => await ExecuteTriggerDoorCommand());
_currentDoorFunc = GetCurrentDoorFunction().Result;
}
private async Task<GPIOFunctions> GetCurrentDoorFunction()
{
return await _client.GetGPIOFunction(DOOR_TOGGLE_PIN);
}
附加信息,class HttpEndpoint
位于单独的 class 库中,并包装了 HttpClient
和所有 API 调用。共享项目和这个 class 库都是 .NET Standard 2.0。
不要将 async/await
与 .Result
等阻塞调用混合使用,这可能会导致死锁。
您可以创建事件和事件处理程序作为解决方法
public MainViewModel() {
_client = new HttpEndpoint(Url, User, Pass);
TriggerDoorCommand = new Command(async () => await ExecuteTriggerDoorCommand());
//Subscribe to event
GetData += GetDataHandler;
//Raise event
GetData(this, EventArgs.Empty);
}
private event EventHandler GetData = delegate { };
private async void GetDataHandler(object sender, EventArgs args) {
_currentDoorFunc = await GetCurrentDoorFunction();
}
private async Task<GPIOFunctions> GetCurrentDoorFunction() {
return await _client.GetGPIOFunction(DOOR_TOGGLE_PIN);
}