UPDATE: Its the conversion from json to the class that is locking the thread. I have tried the same process with a smaller packet of data and the UI didnt freeze. So the question is how to overcome this?
I am trying to get a value from the JSON that is parsed into classes:
public async Task<BitcoinDetails> GetData()
{
return await _httpClient.GetFromJsonAsync<BitcoinDetails>("https://localhost:5001/proxy");
}
I use the OnInitializedAsync to load the data into the view however the following code locks the UI
_bitcoinDetails = new BitcoinDetails();
_bitcoinDetails = await _bitcoinApiService.GetData();
var price = _bitcoinDetails.data.Find(x => x.symbol == "BTC");
if (price == null)
{
_bitcoinPrice = 0;
}
_bitcoinPrice = double.Parse(price.quote.USD.price.ToString());
How do I restructure this code to load the data without locking the UI?
View code:
@if (_bitcoinDetails == null)
{
<p><em>Loading...</em></p>
}
else
{
<h3>BTC:@_bitcoinPrice</h3>
}
Blazor WebAssembly doesn't have real multithreading support yet. All tasks effectively run on the same thread as the UI, which means any CPU-intensive work that takes longer than a few milliseconds to execute may cause noticable freezes in the user interface.
The situation with multithreading in Blazor WebAssembly isn't likely to improve until .NET 6 (November 2021), unfortunately. Until then the workaround is to manually introduce short pauses into a flow of a CPU-intensive task so that the UI can take control during these breaks and do its work:
async Task PerformCpuIntensiveWorkAsync()
{
for (int i = 0; i < 100; i++)
{
PerformOneHundredthOfWork();
await Task.Delay(1);
}
}
Most JSON serializers provide low-level API that gives you full control over the deserialization process:
If you need to deserialize a large JSON, for example, containing an array of 100,000 cars
[
{ "make": "Ford", "model": "Mustang", "year": 2000 },
{ "make": "Honda", "model": "Civic", "year": 2005 },
{ "make": "Chevrolet", "model": "Corvette", "year": 2008 },
...
]
download this JSON at https://api.npoint.io/d159a22063995b37c52d
this is how you can introduce short breaks into the process of deserialization using JSON.Net:
using Newtonsoft.Json;
using System.IO;
...
public class Car
{
public string Make { get; set; }
public string Model { get; set; }
public int Year { get; set; }
}
...
using var jsonStream = await Http.GetStreamAsync("https://api.npoint.io/d159a22063995b37c52d");
List<Car> cars = await DeserializeCarsAsync(jsonStream);
static async Task<List<Car>> DeserializeCarsAsync(Stream jsonStream)
{
using var streamReader = new StreamReader(jsonStream);
using var jsonReader = new JsonTextReader(streamReader);
var serializer = new JsonSerializer();
if (!jsonReader.Read())
throw new JsonException($"Deserialization failed at line {jsonReader.LineNumber} position {jsonReader.LinePosition}.");
if (jsonReader.TokenType != JsonToken.StartArray)
throw new JsonException($"Deserialization failed at line {jsonReader.LineNumber} position {jsonReader.LinePosition}.");
List<Car> cars = new List<Car>();
while (true)
{
if (!jsonReader.Read())
throw new JsonException($"Deserialization failed at line {jsonReader.LineNumber} position {jsonReader.LinePosition}.");
if (jsonReader.TokenType == JsonToken.EndArray)
return cars;
if (jsonReader.TokenType != JsonToken.StartObject)
throw new JsonException($"Deserialization failed at line {jsonReader.LineNumber} position {jsonReader.LinePosition}.");
var car = serializer.Deserialize<Car>(jsonReader);
cars.Add(car);
// Pause after every 10th deserialized car:
if (cars.Count % 10 == 0)
await Task.Delay(1);
}
}
It does look overcomplicated and gets even worse if you have to deal with nested collections, but it solves the problem.
Work with smaller JSON, if possible. It looks like you're either fetching quotes or listings from CoinMarketCap using a proxy. You get the entire list but only need one item - BTC. It's hard to say without knowing the details whether this will suit you, but it's possible to ask CoinMarketCap server to filter the results for you and only return the data for the BTC quote - this would result in much smaller JSON: https://sandbox-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest?CMC_PRO_API_KEY=b54bcf4d-1bca-4e8e-9a24-22ff2c3d462c&symbol=BTC .
Try faster JSON serializer. Utf8Json looks promising.
If you need to deserialize only a few fields from a large JSON, there's many potential optimizations:
Quote
objects and only need to obtain their prices, try using a class with the only property Price
to deserialize to: class Quote { decimal Price {get; set;} }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With