Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,15 @@ public async ValueTask DisposeAsync()
{
Disposed = true;

await _jsRuntime.InvokeVoidAsync("DotNet.disposeJSObjectReferenceById", Id);
try
{
await _jsRuntime.InvokeVoidAsync("DotNet.disposeJSObjectReferenceById", Id);
}
catch (JSDisconnectedException)
{
// If the JavaScript runtime is disconnected, there's no need to dispose the JS object reference
// as the JS side is already gone. We can safely ignore this exception.
}
}
}

Expand Down
51 changes: 51 additions & 0 deletions src/JSInterop/Microsoft.JSInterop/test/JSObjectReferenceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,37 @@ public void JSInProcessObjectReference_Dispose_DisallowsFurtherInteropCalls()
Assert.Throws<ObjectDisposedException>(() => jsObject.Invoke<object>("test", "arg1", "arg2"));
}

[Fact]
public async Task JSObjectReference_DisposeAsync_IgnoresJSDisconnectedException()
{
// Arrange
var jsRuntime = new TestJSRuntimeThatThrowsJSDisconnectedException();
var jsObject = new JSObjectReference(jsRuntime, 0);

// Act & Assert - Should not throw
await jsObject.DisposeAsync();

// Verify dispose was attempted
Assert.Equal(1, jsRuntime.BeginInvokeJSInvocationCount);
}

[Fact]
public async Task JSObjectReference_DisposeAsync_IgnoresJSDisconnectedException_OnMultipleCalls()
{
// Arrange
var jsRuntime = new TestJSRuntimeThatThrowsJSDisconnectedException();
var jsObject = new JSObjectReference(jsRuntime, 0);

// Act & Assert - Should not throw on first call
await jsObject.DisposeAsync();

// Act & Assert - Should not throw on second call (no-op)
await jsObject.DisposeAsync();

// Verify dispose was only attempted once
Assert.Equal(1, jsRuntime.BeginInvokeJSInvocationCount);
}

class TestJSRuntime : JSRuntime
{
public int BeginInvokeJSInvocationCount { get; private set; }
Expand All @@ -85,6 +116,26 @@ protected internal override void EndInvokeDotNet(DotNetInvocationInfo invocation
}
}

class TestJSRuntimeThatThrowsJSDisconnectedException : JSRuntime
{
public int BeginInvokeJSInvocationCount { get; private set; }

protected override void BeginInvokeJS(in JSInvocationInfo invocationInfo)
{
BeginInvokeJSInvocationCount++;
throw new JSDisconnectedException("JavaScript interop calls cannot be issued at this time. This is because the circuit has disconnected and is being disposed.");
}

protected override void BeginInvokeJS(long taskId, string identifier, [StringSyntax("Json")] string? argsJson, JSCallResultType resultType, long targetInstanceId)
{
throw new NotImplementedException();
}

protected internal override void EndInvokeDotNet(DotNetInvocationInfo invocationInfo, in DotNetInvocationResult invocationResult)
{
}
}

class TestJSInProcessRuntime : JSInProcessRuntime
{
public int InvokeJSInvocationCount { get; private set; }
Expand Down
Loading