Multithreading in Blazor WebAssembly
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);
}
}
Deserialization of Large JSON
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.
Other Options
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:
- Try deserializing into a class with fewer fields. E.g., if you deserialize
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;} }
- Try deserializing only the specific JSON nodes that you need using JsonDocument. It would still require parsing the entire JSON first to create a map of its nodes though, but anyway it should be faster than deserializing the entire JSON.
- If you use a serializer's low-level API, most of them allow skipping JSON elements while parsing (JsonReader.Skip, Utf8JsonReader.Skip, JsonReader.ReadNextBlock, etc.).