Skip to content

Commit 1aa0c99

Browse files
committed
Component lifecycle and disposal concepts
1 parent bd53f64 commit 1aa0c99

File tree

2 files changed

+13
-1
lines changed

2 files changed

+13
-1
lines changed

aspnetcore/blazor/components/lifecycle.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -763,7 +763,7 @@ Although the content in this section focuses on Blazor Server and stateful Signa
763763

764764
## Component disposal with `IDisposable` and `IAsyncDisposable`
765765

766-
If a component implements <xref:System.IDisposable> or <xref:System.IAsyncDisposable>, the framework calls for resource disposal when the component is removed from the UI. Don't rely on the exact timing of when these methods are executed. For example, <xref:System.IAsyncDisposable> can be triggered before or after an asynchronous <xref:System.Threading.Tasks.Task> awaited in [`OnInitalizedAsync`](#component-initialization-oninitializedasync) is called or completes. Also, object disposal code shouldn't assume that objects created during initialization or other lifecycle methods exist.
766+
If a component implements <xref:System.IDisposable> or <xref:System.IAsyncDisposable>, the framework calls for resource disposal when the component is removed from the UI. Don't rely on the exact timing of when these methods are executed. For example, <xref:System.IAsyncDisposable> can be triggered before or after an asynchronous <xref:System.Threading.Tasks.Task> awaited in [`OnInitalizedAsync`](#component-initialization-oninitializedasync) or [`OnParametersSetAsync`](#after-parameters-are-set-onparameterssetasync) is called or completes. Also, object disposal code shouldn't assume that objects created during initialization or other lifecycle methods exist.
767767

768768
Components shouldn't need to implement <xref:System.IDisposable> and <xref:System.IAsyncDisposable> simultaneously. If both are implemented, the framework only executes the asynchronous overload.
769769

aspnetcore/blazor/components/synchronization-context.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,18 @@ Blazor uses a synchronization context (<xref:System.Threading.SynchronizationCon
1616

1717
Blazor's server-side synchronization context attempts to emulate a single-threaded environment so that it closely matches the WebAssembly model in the browser, which is single threaded. This emulation is scoped only to an individual circuit, meaning two different circuits can run in parallel. At any given point in time within a circuit, work is performed on exactly one thread, which yields the impression of a single logical thread. No two operations execute concurrently within the same circuit.
1818

19+
A single logical thread of execution doesn't imply a single asynchronous control flow. A component is reentrant at any point where it awaits an incomplete <xref:System.Threading.Tasks.Task>. [Lifecycle methods](xref:blazor/components/lifecycle) or [`Dispose`/`IAsyncDisposable`](xref:blazor/components/lifecycle#component-disposal-with-idisposable-and-iasyncdisposable) may be called before the asynchronous control flow resumes after awaiting a <xref:System.Threading.Tasks.Task> to complete. Therefore, a component must ensure that it's in a valid state before awaiting any potentially incomplete <xref:System.Threading.Tasks.Task>. In particular, a component must ensure that it's in a valid state for rendering when <xref:Microsoft.AspNetCore.Components.ComponentBase.OnInitializedAsync%2A> or <xref:Microsoft.AspNetCore.Components.ComponentBase.OnParametersSetAsync%2A> return. If either of these methods return an incomplete <xref:System.Threading.Tasks.Task>, they must ensure that the part of the method that completes synchronously leaves the component in a valid state for rendering.
20+
21+
Another implication of reentrant components is that a method can't defer a <xref:System.Threading.Tasks.Task> until after the method returns by passing it to <xref:Microsoft.AspNetCore.Components.ComponentBase.InvokeAsync%2A?displayProperty=nameWithType>. Calling <xref:Microsoft.AspNetCore.Components.ComponentBase.InvokeAsync%2A?displayProperty=nameWithType> may only defer the <xref:System.Threading.Tasks.Task> until the next [`await` operator](/dotnet/csharp/language-reference/operators/await) is reached.
22+
23+
Components might implement <xref:System.IDisposable> or <xref:System.IAsyncDisposable> to call asynchronous methods using a <xref:System.Threading.CancellationToken> from a <xref:System.Threading.CancellationTokenSource> that's canceled when the component is disposed. However, this really depends on the scenario. It's up to the component author to determine whether that's the correct behavior. For example, if implementing a `SaveButton` component that persists some local data to a database when a save button is selected, the component author may indeed intend to discard the changes if the user selects the button and quickly navigates to another page, which could dispose the component before the asynchronous save completes.
24+
25+
A disposable component can check for disposal after awaiting any <xref:System.Threading.Tasks.Task> that doesn't receive the component's <xref:System.Threading.CancellationToken>. Incomplete <xref:System.Threading.Tasks.Task>s may also prevent garbage collection of a disposed component.
26+
27+
<xref:Microsoft.AspNetCore.Components.ComponentBase> ignores exceptions caused by <xref:System.Threading.Tasks.Task> cancellation (more precisely, it ignores *all* exceptions if the awaited <xref:System.Threading.Tasks.Task>s are canceled), so component methods don't need to handle <xref:System.Threading.Tasks.TaskCanceledException> and <xref:System.OperationCanceledException>.
28+
29+
<xref:Microsoft.AspNetCore.Components.ComponentBase> can't follow the preceding guidelines because it doesn't conceptualize what constitutes a valid state for a derived component and it doesn't itself implement <xref:System.IDisposable> or <xref:System.IAsyncDisposable>. If <xref:Microsoft.AspNetCore.Components.ComponentBase.OnInitializedAsync%2A> returns an incomplete <xref:System.Threading.Tasks.Task> that doesn't use a <xref:System.Threading.CancellationToken> and the component is disposed before the <xref:System.Threading.Tasks.Task> completes, <xref:Microsoft.AspNetCore.Components.ComponentBase> still calls <xref:Microsoft.AspNetCore.Components.ComponentBase.OnParametersSet%2A> and awaits <xref:Microsoft.AspNetCore.Components.ComponentBase.OnParametersSetAsync%2A>. If a disposable component doesn't use a <xref:System.Threading.CancellationToken>, <xref:Microsoft.AspNetCore.Components.ComponentBase.OnParametersSet%2A> and <xref:Microsoft.AspNetCore.Components.ComponentBase.OnParametersSetAsync%2A> should check if the component is disposed.
30+
1931
## Avoid thread-blocking calls
2032

2133
Generally, don't call the following methods in components. The following methods block the execution thread and thus block the app from resuming work until the underlying <xref:System.Threading.Tasks.Task> is complete:

0 commit comments

Comments
 (0)