为 Blazor 中的嵌套(不是第一个或最后一个)方法维护 SynchronizationContext
Maintaining SynchronizationContext for nested (not first or last) methods in Blazor
在 .NetCore 3.1 中创建新的 Blazor 服务器端项目时,使用加载 Visual Studio 2019 的默认项目,请考虑以下示例(对他们自己的计数器示例页面略有更改):
<button class="btn btn-primary" @onclick="SingleMethodUpdate">Click Me</button>
<label>Update from same method: @result</label>
<button class="btn btn-primary" @onclick="NestedMethodUpdate">Click Me</button>
<label>Update from nested method: @nestedResult</label>
代码隐藏看起来像这样:
int result = 0;
int nestedResult = 0;
int nestedCounter = 0;
protected async Task SingleMethodUpdate()
{
result++;
}
protected async Task NestedMethodUpdate()
{
await NestedUpdate(++nestedCounter);
await Task.Delay(1000);
await NestedUpdate(++nestedCounter);
await Task.Delay(1000);
await NestedUpdate(++nestedCounter);
}
protected Task NestedUpdate(int update)
{
nestedResult = update;
return Task.FromResult(0);
}
第一个按钮和标签很简单,按一个按钮,UI 上的计数会按预期立即更新。不过第二个按钮比较复杂
中间的 await NestedUpdate(++nestedCounter)
调用没有反映在 UI 上。第一次调用和最后一次调用(第一次和最后一次嵌套方法调用)都实时反映在UI上,所以你看到的是:
Update from nested method: 1
*2 seconds go by
Update from nested method: 3
我意识到这是因为 SynchronizationContext
的警告,但我更好奇我将如何着手捕获和 维护 该上下文,以便每个 awaited
在我的按钮按下事件中调用的方法使用相同的上下文,因此 UI 将在代码执行时实时更新。有 nice/pretty 的方法吗?
I realize this is because of the caveats with SynchronizationContext
不,不是真的。所有这些代码都应该 运行 异步但在主线程上顺序执行。
此处的 SyncContext 没有任何问题或需要修复。
您看到的是由于 StateHasChanged() 逻辑,它实际上是关于一个状态,大概只是一个布尔标志。
Blazor 框架将在调用您的事件处理程序之前和之后设置此标志,但在中间您必须自己这样做。
快速修复和解释:
protected async Task NestedMethodUpdate()
{
// the flag is pre-set here
await NestedUpdate(++nestedCounter);
// the thread is available and Blazor will render '1' and reset the flag
await Task.Delay(1000);
await NestedUpdate(++nestedCounter);
// the thread is available but the flag is down: the '2' is not shown
await Task.Delay(1000);
await NestedUpdate(++nestedCounter);
StatehasChanged(); // <<-- add this
// the thread is available and the flag is up: the '3' is shown
await Task.Delay(1000);
// you can add more steps
await NestedUpdate(++nestedCounter);
StatehasChanged(); // <<-- set the flag
await Task.Delay(1000); // show '4'
// this result will show '5' after the method has completed
await NestedUpdate(++nestedCounter);
}
在 .NetCore 3.1 中创建新的 Blazor 服务器端项目时,使用加载 Visual Studio 2019 的默认项目,请考虑以下示例(对他们自己的计数器示例页面略有更改):
<button class="btn btn-primary" @onclick="SingleMethodUpdate">Click Me</button>
<label>Update from same method: @result</label>
<button class="btn btn-primary" @onclick="NestedMethodUpdate">Click Me</button>
<label>Update from nested method: @nestedResult</label>
代码隐藏看起来像这样:
int result = 0;
int nestedResult = 0;
int nestedCounter = 0;
protected async Task SingleMethodUpdate()
{
result++;
}
protected async Task NestedMethodUpdate()
{
await NestedUpdate(++nestedCounter);
await Task.Delay(1000);
await NestedUpdate(++nestedCounter);
await Task.Delay(1000);
await NestedUpdate(++nestedCounter);
}
protected Task NestedUpdate(int update)
{
nestedResult = update;
return Task.FromResult(0);
}
第一个按钮和标签很简单,按一个按钮,UI 上的计数会按预期立即更新。不过第二个按钮比较复杂
中间的 await NestedUpdate(++nestedCounter)
调用没有反映在 UI 上。第一次调用和最后一次调用(第一次和最后一次嵌套方法调用)都实时反映在UI上,所以你看到的是:
Update from nested method: 1
*2 seconds go by
Update from nested method: 3
我意识到这是因为 SynchronizationContext
的警告,但我更好奇我将如何着手捕获和 维护 该上下文,以便每个 awaited
在我的按钮按下事件中调用的方法使用相同的上下文,因此 UI 将在代码执行时实时更新。有 nice/pretty 的方法吗?
I realize this is because of the caveats with SynchronizationContext
不,不是真的。所有这些代码都应该 运行 异步但在主线程上顺序执行。
此处的 SyncContext 没有任何问题或需要修复。
您看到的是由于 StateHasChanged() 逻辑,它实际上是关于一个状态,大概只是一个布尔标志。
Blazor 框架将在调用您的事件处理程序之前和之后设置此标志,但在中间您必须自己这样做。
快速修复和解释:
protected async Task NestedMethodUpdate()
{
// the flag is pre-set here
await NestedUpdate(++nestedCounter);
// the thread is available and Blazor will render '1' and reset the flag
await Task.Delay(1000);
await NestedUpdate(++nestedCounter);
// the thread is available but the flag is down: the '2' is not shown
await Task.Delay(1000);
await NestedUpdate(++nestedCounter);
StatehasChanged(); // <<-- add this
// the thread is available and the flag is up: the '3' is shown
await Task.Delay(1000);
// you can add more steps
await NestedUpdate(++nestedCounter);
StatehasChanged(); // <<-- set the flag
await Task.Delay(1000); // show '4'
// this result will show '5' after the method has completed
await NestedUpdate(++nestedCounter);
}