Blazor Cancellation Token | C# Performance Optimierung

Blazor Cancellation Token: Performance-Tuning und UX-Verbesserung
Darius S. | Softwareentwickler
10/2024

Cancellation Token für bessere Performance in Blazor mit C#.

Bei Webanwendungen und überall dort, wo HTTP-Requests gesendet werden, wird asynchrone Programmierung eingesetzt. Diese bietet zahlreiche Vorteile, wie eine flüssige Benutzeroberfläche, verbesserte Performance und parallele Zugriffe auf WebAPIs. Wenn man mit C# asynchron programmiert, stößt man früher oder später auf die Parameter asynchroner Funktionen, insbesondere auf das Cancellation Token. Die Idee dahinter ist einfach: Wenn eine asynchrone Methode ein Cancellation Token übergeben bekommt, kann die Ausführung der Methode vorzeitig abgebrochen werden.

Wofür wird ein Cancellation Token verwendet?

Ein einfaches Szenario ist ein Suchfeld mit einer großen Datenmenge. Der Benutzer gibt eine Zeichenfolge in das Suchfeld ein, und bei jedem neuen Buchstaben wird die Suche erneut durchgeführt. Ohne Cancellation Token wird jede Suche vollständig ausgeführt. Tippt ein Benutzer ein Wort, werden Suchergebnisse der einzelnen Buchstabenfragmente angezeigt, bevor das endgültige Suchergebnis vorliegt. Bei einfachen Suchanfragen wird zumindest die Wartezeit nicht maßgeblich beeinträchtigt. Wenn jedoch komplexe Berechnungen erforderlich sind, würden diese für jeden Buchstaben durchgeführt werden. In solchen Fällen kann ein Cancellation Token zu einer deutlichen Performance-Verbesserung führen.

Die Anwendung von Cancellation Tokens möchte ich an einem kleinen Beispiel verdeutlichen. Das Projekt ist hier zu finden:
GitHub: CancellationToken C# Blazor Demo

In diesem Szenario wurde eine Blazor-Webanwendung mit WebAPI entwickelt. Als Beispiel dient die Standard-‘Weather Forecast‘-Seite der Blazor-Vorlage. In der WebAPI wurde ein Controller hinzugefügt, der nach einer Verzögerung zufällige Wetterdaten zurückgibt. Der Zugriff auf die WebAPI erfolgt über einen Proxy (WeatherForecastProxy). Controller und Proxy implementieren beide das zugehörige Interface (IWeatherForecastExchange).

Dieser Programmfluss wurde zweimal implementiert: einmal ohne und einmal mit Cancellation Token. So können beide Szenarien, sowohl auf der Quellcode- als auch auf der Anwenderseite, miteinander verglichen werden.

Auf der Demo-‘Weather Forecast‘-Seite wurde ein Texteingabefeld hinzugefügt, welches auf das oninput-Blazor-Event reagiert. Wenn der Eingabetext geändert wird, soll von der API ein neuer zufälliger Wetterdatensatz abgerufen werden. In der Komponente Weather.razor (ohne Cancellation Token) wird über den Proxy jeweils ein Request gesendet, und entsprechend ändern sich die Daten im Frontend.

Um mit Cancellation Tokens zu arbeiten, benötigen wir in C# ein Objekt vom Typ CancellationTokenSource. In der textChanged-Methode der Komponente WeatherWithCancellation.razor wird der Token des vorherigen Aufrufs abgebrochen und eine neue CancellationTokenSource erstellt, deren Token an den Proxy übergeben wird.

Aufruf des Proxys mit Cancellation Token

1@code {
2    private List<WeatherForecastDTO>? forecasts;
3
4    private CancellationTokenSource _cts = new CancellationTokenSource();
5
6    protected override async Task OnInitializedAsync()
7    {
8        forecasts = await weatherForecastExchange.GetAll(DateTime.Now, _cts.Token);
9    }
10
11    private async Task textChanged(ChangeEventArgs e)
12    {
13        _cts.Cancel();
14        _cts = new CancellationTokenSource();
15        try
16        {
17            forecasts = await weatherForecastExchange.GetAll(DateTime.Now, _cts.Token);
18        }
19        catch (TaskCanceledException ex)
20        {
21            Console.WriteLine($"Task Canceled: {ex.Message}");
22        }
23        catch (Exception ex)
24        {
25            Console.WriteLine(ex.Message);
26        }
27    }
28}

Damit auf ein Cancellation Token reagiert werden kann, muss es in jede aufgerufene asynchrone Methode weitergegeben werden. Dementsprechend wird im Proxy und auch im Controller das Cancellation Token an die Service-Klasse, welche die Wetterdaten ermittelt, weitergereicht.

Proxy-Methode (Übergabe des Cancellation Tokens)

1public async Task<List<WeatherForecastDTO>> GetAll(DateTime startDate, System.Threading.CancellationToken cancellationToken)
2{
3    try
4    {
5        var result = await httpClient.GetFromJsonAsync<List<WeatherForecastDTO>>("/api/WeatherForecast", cancellationToken);
6        if (result != null)
7        {
8            return result;
9        }
10        return new List<WeatherForecastDTO>();
11    }
12    catch (Exception ex)
13    {
14        await Console.Out.WriteLineAsync(ex.Message);
15        throw;
16    }
17}

Controller-Methode (Übergabe des Cancellation Tokens)

1[HttpGet]
2public async Task<List<WeatherForecastDTO>> GetAll(DateTime startDate, System.Threading.CancellationToken cancellationToken)
3{
4    try
5    {
6                
7        await Task.Delay(1000, cancellationToken);
8        return Enumerable.Range(1, 5).Select(index => new WeatherForecastDTO
9        {
10            Date = startDate.AddDays(index),
11            TemperatureC = Random.Shared.Next(-20, 55),
12            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
13        }).ToList();
14    }
15    catch (Exception ex)
16    {
17        throw;
18    }
19}

Um die Funktionsweise des Cancellation Tokens besser nachvollziehen zu können, habe ich die Fehler protokolliert. Wird ein Cancellation Token abgebrochen, so wird eine TaskCanceledException ausgelöst. Auf diese kann sowohl auf der API- als auch auf der Frontend-Seite reagiert werden. Tippt man bei diesem Testprojekt auf der Weather Cancellation-Seite in das Suchfeld, sieht man in der Konsole, dass dieser Fehler protokolliert wird.

Im Vergleich zur Seite ohne Cancellation Token sieht man hier, dass bei schnellem Tippen in das Suchfeld die Daten nur einmalig aktualisiert werden und die angezeigten Daten nicht hinter dem Eingabetempo des Benutzers zurückbleiben.

Mehr zum Thema

Pfeil nach rechts (Verlinkung)
FluentValidation in Blazor: Validierungslogik mit Custom Rules und Feldern
04/2025

Blazor FluentValidation | C# Validation Best Practices

Pfeil nach rechts (Verlinkung)
Liskov Principle: Schnittstellenfehler & Sicherheitslücken vermeiden
12/2024

Solid Reihe - Liskov Substitution Principle

Pfeil nach rechts (Verlinkung)
Open/Closed Principle: Erweiterbare Software dank klarer Struktur
11/2024

Solid Reihe - Open Close Principle

Pfeil nach rechts (Verlinkung)
JavaScript & Blazor kombinieren: [JSInvokable] Funktionen in .NET-Apps nutzen
11/2024

Interoperabilität JavaScript und Blazor

Pfeil nach rechts (Verlinkung)

Gemeinsam Großes schaffen

Devware GmbH verpflichtet sich, Ihre Privatsphäre zu schützen. Wir benötigen Ihre Kontaktinformationen, um Sie bezüglich unserer Produkte und Dienstleistungen zu kontaktieren. Mit Klick auf Absenden geben Sie sich damit einverstanden. Weitere Informationen finden Sie unter Datenschutz.
Vielen Dank für Ihr Vertrauen.
Unser Team prüft Ihre Anfrage sorgfältig und meldet sich in der Regel innerhalb von 48 Stunden bei Ihnen zurück.
Falls es besonders eilig ist, erreichen Sie uns auch telefonisch:
+ 49 (0) 202 478 269 0.
Da ist etwas schief gegangen beim Absenden des Formulars.