Google search on this topic only gives 4 results, one mention from MudBlazor
I have the following @code in the bottom of a .razor file:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
CancellationTokenSource cts = new();
var token = cts.Token;
_CTSList.Add(cts);
try
{
if (cts.IsCancellationRequested)
{
cts.Token.ThrowIfCancellationRequested();
}
if (_elementRef.Id is not null)
{
await JS.InvokeVoidAsync("MyJsFunction", cts.Token);
}
}
catch {
}
finally
{
_CTSList.Remove(cts);
cts.Dispose();
}
}
public async ValueTask DisposeAsync()
{
foreach (var cts in _CTSList)
{
cts.Cancel();
Debug.WriteLine("a cts has been canceled ");
}
await JS.InvokeVoidAsync("MyCleanUpFunction");
}
Basically, there is some JS interop that should get executed at certain events, and it seems it's the Blazor way, that JS interop has to wait until OnAfterRenderAsync
in most cases, especially when ElementReference
s are used.
Ofc when the user navigates away from the page, that JS should no longer be invoked, and ElementReference
s not more referenced (will cause errors). Also I want to run some cleanup JS code, like setting some vars to null in JS when they are no longer needed (when navigating away).
I tried CancelationTokens, but even they seem to have a problem, and are probably even unnecessary. I'd like to have general, solid pattern for this.
The main problem however is that there is concurrency between OnAfterRenderAsync
and DisposeAsync
. Or call it a race condition between those async methods.
While testing, I actually managed, that during the short foreach
loop in DisposeAsync
that OnAfterRenderAsync
was deleting items from the List, causing an access violation.
In my mind this is very counter intuitive. How can OnAfterRenderAsync still run, when the razor component is already disposing??? Obviously we would not want that ??? I', now thinking, if maybe a lock
around both method bodies would help, or maybe another pattern is necessary for what I want.
Background: In JS I have an ResizeObserver that needs disposing (I guess, cuz its observed Element gets removed from the DOM when navigating away), and should ofc NOT be created again in an OnAfterRenderAsync
AFTER DisposeAsync
has run. DisposeAsync
MUST run last (not simultaneously).
So would a lock help? What else can be done? Why is this so horribly complicated, is there a better way to do JS stuff like this?
•MudPopover: Fixed leaks caused by concurrency between OnAfterRenderAsync and DisposeAsync #3963
MainLayout
level or higher your app has effectively been disposed so does it matter? – Endmostlock
. So that once DisposeAsync has run, nothing OnAfterRender exists immediately and does no more work, or maybe even tries disposing itself. Atm the issue is only a problem if inside OnAfterRender, after a valid flag check, there is artificially Task.Delay, so that there is time for DisposeAsync to squat in. The above steps, prevent that. Let me know, if there is more problems. – Dufrene