简短的回答:是的。
更长的答案:
从 C# 到 JavaScript 的调用会立即执行,反之亦然。
然后由 Render Dispatcher 处理对 C# 的调用,这意味着如果 C# 代码是同步的(或一直异步与等待),则对 C# 的每个调用将在下一个调用开始之前运行完成。
如果 C# 代码是异步的并且不等待异步操作(例如Task.Delay
),则 Render Dispatcher 将在当前调用的方法等待后立即开始运行下一个调用的方法。
多个线程不会同时运行。await
在当前调度的任务执行await
或完成之前,渲染调度程序不会继续执行代码。
实际上,Render Dispatcher 序列化了对组件的访问,因此任何组件上一次只运行 1 个线程——不管它们上运行了多少线程。
PS:您可以通过使用InvokeAsync(......)
由外部刺激触发的任何代码来执行相同的操作,例如由 Singleton 服务上另一个用户的线程引发的事件。
如果您想了解有关 Render Dispatcher 如何工作的更多信息,请阅读Blazor University 上的多线程渲染部分。
这里有一些证据:
首先,创建 index.js 并确保它在您的 HTML 页面中被引用<script src="/whatever/index.js"></script>
window.callbackDotNet = async function (objRef, counter) {
await objRef.invokeMethodAsync("CalledBackFromJavaScript", counter);
}
接下来,更新Index.razor
页面,使其调用该 JavaScript 并接受回调。
@page "/"
@inject IJSRuntime JSRuntime
<button @onclick=ButtonClicked>Click me</button>
@code
{
private async Task ButtonClicked()
{
using (var objRef = DotNetObjectReference.Create(this))
{
const int Max = 10;
for (int i = 1; i < 10; i++)
{
System.Diagnostics.Debug.WriteLine("Call to JS " + i);
await JSRuntime.InvokeVoidAsync("callbackDotNet", objRef, i);
}
System.Diagnostics.Debug.WriteLine("Call to JS " + Max);
await JSRuntime.InvokeVoidAsync("callbackDotNet", objRef, Max);
}
}
[JSInvokable("CalledBackFromJavaScript")]
public async Task CalledBackFromJavaScript(int counter)
{
System.Diagnostics.Debug.WriteLine("Start callback from JS call " + counter);
await Task.Delay(1000).ConfigureAwait(false);
System.Diagnostics.Debug.WriteLine("Finish callback from JS call " + counter);
}
}
等待整个链,包括 JavaScript,所以输出将如下所示......
Call to JS 1
Start callback from JS call 1
* (one second later)
Finish callback from JS call 1
Call to JS 2
Start callback from JS call 2
* (one second later)
Finish callback from JS call 2
... etc ...
Call to JS 9
Start callback from JS call 9
* (one second later)
Finish callback from JS call 9
Call to JS 10
Start callback from JS call 10
* (one second later)
Finish callback from JS call 10
如果你从你的 JavaScript中删除async
and ,像这样await
window.callbackDotNet = function (objRef, counter) {
objRef.invokeMethodAsync("CalledBackFromJavaScript", counter);
}
当您运行它时,您会看到对 JavaScript 的调用以正确的 1..10 顺序进行,而对 C# 的回调以正确的 1..10 顺序进行,但“完成回调”的顺序是 2、1、4, 3,5,7,6,9,8,10。
这将归因于 C# 内部 .NET 调度等,其中 .NET 决定在所有这些之后接下一个任务await Task.Delay(1000)
。
如果您在 JavaScript 中恢复async
andawait
然后仅await
在最后一次 C# 调用中恢复,如下所示:
using (var objRef = DotNetObjectReference.Create(this))
{
const int Max = 10;
for (int i = 1; i < 10; i++)
{
System.Diagnostics.Debug.WriteLine("Call to JS " + i);
_ = JSRuntime.InvokeVoidAsync("callbackDotNet", objRef, i);
}
System.Diagnostics.Debug.WriteLine("Call to JS " + Max);
await JSRuntime.InvokeVoidAsync("callbackDotNet", objRef, Max);
}
然后您将看到以下输出:
Call to JS 1
Call to JS 2
Call to JS 3
Call to JS 4
Start callback from JS call 1
Start callback from JS call 2
Start callback from JS call 3
Start callback from JS call 4
* (one second later)
Finish callback from JS call 1
Finish callback from JS call 2
Finish callback from JS call 3
Finish callback from JS call 4
注意:您的示例代码会导致多个用户同时执行一个静态方法。在您的场景中,您应该调用实例方法。