-
Notifications
You must be signed in to change notification settings - Fork 10.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add DispatchExceptionAsync to ComponentBase #46074
Conversation
I've been playing with Selenium/E2E testing for a few days and this appears to be the cleanest way to test this scenario if I'm understanding it correctly |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice!
} | ||
} | ||
|
||
private Task ThrowExceptionAsync() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not throwing the exception asynchronously. The thread is not yielded at any point, so this is similar/equivalent to the SyncExceptionDispatch
case.
For the exception to be thrown asynchronously you need to use something like await Task.Yield()
before throwing the exception.
Also, I will point out that we normally also write unit tests for these types of behavior in the Renderer itself. There we normally use a TaskCompletionSource to very accurately control the process.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point.
In fact more generally I think these test cases are missing the key scenario: when an exception happens outside the normal lifecycle flow and you couldn't just do a normal throw
anyway. With the way SyncExceptionDispatch
and AsyncExceptionDispatch
are structured, DispatchExceptionAsync
is no different from simply using throw
at that point, because we're still within the task chain of the lifecycle method.
It would be good to cover the primary use case where this new feature is what causes the exception to be associated with the renderer. For example, by using Task.Run
to simulate having some code running completely separately from the renderer callstack/task chain:
void ExternalExceptionDispatch()
{
Task.Run(() =>
{
// Inside Task.Run, we're outside the call stack or task chain of the lifecycle method, so
// DispatchExceptionAsync is needed to get an exception back into the component
_ = DispatchExceptionAsync(new InvalidTimeZoneException());
});
}
If we are going to have the SyncExceptionDispatch
/AsyncExceptionDispatch
cases, I imagine they would be there to cover the case where people are calling DispatchExceptionAsync
even though they didn't need to (i.e., calls that are already on the lifecycle method call stack), so they could be simplified to just dispatching a newed-up exception like:
async Task SyncExceptionDispatch()
{
await DispatchExceptionAsync(new InvalidTimeZoneException("Sync"));
}
async Task AsyncExceptionDispatch()
{
await Task.Yield();
await DispatchExceptionAsync(new InvalidTimeZoneException("Async"));
}
Co-authored-by: Steve Sanderson <SteveSandersonMS@users.noreply.github.com>
@SteveSandersonMS @javiercn I should have test(s) for the new, correct testing scenario that you guys described completed soon. |
I totally understand that it's nonobvious, but For more general context, |
Add DispatchExceptionAsync to ComponentBase
Adds way for developers to dispatch exceptions outside of the Blazor sync context to the renderer.
Description
DispatchExceptionAsync
sends exceptions from theComponentBase
to theRenderer
via theRenderHandle
. See below proposal for more info.Fixes #44920
API Proposal: #46068