问题描述
public ObservableCollection<Magnetka> ParseJSON() {
ObservableCollection<Models.Magnetka> Markers = new ObservableCollection<Models.Magnetka>();
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("(URL)");
request.Headers.Add("Authentication-Token","(KEY)");
request.Method = "GET";
request.ContentType = "application/json";
CookieContainer cookieContainer = new CookieContainer();
request.CookieContainer = cookieContainer;
var response = request.GetResponse();
var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
......... JSON parsing)
return Markers;
现在我发现这并不太快,它会冻结应用程序两秒钟左右,所以我需要使它异步。但是,我在另一个类中这样调用方法:
GetMarkers getMarkers = new GetMarkers();
Markers = getMarkers.ParseJSON();
如何使我的方法异步?我不太理解这个概念,如果我让 ParseJSON() 异步任务,我不知道把“await”关键字放在哪里。 有人可以帮我吗?
解决方法
首先,标记方法async
并使用GetResponse
的异步版本
public async Task<ObservableCollection<Magnetka>> ParseJSON()
{
...
var response = await request.GetResponseAsync();
...
return Markers;
}
然后当你调用它时使用 await
关键字
Markers = await getMarkers.ParseJSON();
,
你可以将你的方法改为这样:
public async Task<ObservableCollection<Magnetka>> ParseJSON()
{
var markers = new ObservableCollection<Magnetka>();
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("(URL)");
request.Headers.Add("Authentication-Token","(KEY)");
request.Method = "GET";
request.ContentType = "application/json";
CookieContainer cookieContainer = new CookieContainer();
request.CookieContainer = cookieContainer;
using var response = await request.GetResponseAsync().ConfigureAwait(false);
using var responseStream = response.GetResponseStream();
using var streamReader = new StreamReader(responseStream);
var json = await streamReader.ReadToEndAsync().ConfigureAwait(false);
// json parsing
return markers;
}
首先,将方法标记为异步。这表明编译器为此方法设置状态机以进行继续。不过还没有发生任何异步事件。
您需要做的另一件事是将您的返回值包装在 Task
中,这类似于其他一些语言中的承诺。任务是 await
-able,这是发生异步事情的地方。
从使用 WebRequest 中的同步方法切换到使用 Async 变体并等待它们的结果会使您的方法异步。
所以来自:
request.GetResponse()
致:
await request.GetResponseAsync()
返回一个可等待的 Task<WebResponse>
。
流阅读器代码也是如此,您也可以使用其异步 API。
使用异步代码的一个棘手问题是,它迫使您一直这样做。但是,生命周期方法和事件也有例外,您不必更改签名即可返回 Task
。
您可能会在这里注意到的另一件事。我在异步方法调用的末尾添加了 ConfigureAwait(false)
。我这样做是因为开箱即用,从等待的方法返回后,代码将尝试返回到原始上下文。如果原始上下文是 UI 线程,这可能会导致一些问题,例如死锁。或者,尝试在上下文之间进行大量切换可能会导致性能不佳。添加 ConfigureAwait(false)
只会在运行 await 的相同上下文中返回,只要我们不需要或期望在该上下文中返回就可以。
至于在何处调用 ParseJson()
可能同样重要。考虑将其推迟到某个生命周期方法或事件中,而您没有做太多其他事情。
这在 Android 上可能是 OnResume()
,在 iOS 上可能是 ViewWillAppear
。如果你使用的是MVVM,使用一个Command来封装它。