简短回答:是的。
更长的答案:
从 C# 到 JavaScript 的调用(反之亦然)会立即执行。
然后由 Render Dispatcher 处理返回到 C# 的调用,这意味着如果 C# 代码是同步的(或一直异步的等待),那么每次对 C# 的调用将在下一个调用开始之前运行到完成。
如果 C# 代码是异步的并且不等待异步操作(例如 Task.Delay),则渲染调度程序将在当前方法等待时开始运行下一个调用的方法。
多个线程不会同时运行。在当前调度的任务执行await 或完成之前,Render Dispatcher 不会在 await 之后继续执行代码。
实际上,Render Dispatcher 序列化了对组件的访问,因此在任何组件上一次只运行 1 个线程 - 而不管它们上运行了多少线程。
PS:您可以对任何由外部刺激触发的代码(例如由 Singleton 服务上的另一个用户线程引发的事件)使用 InvokeAsync(......)。
如果您想了解有关 Render Dispatcher 工作原理的更多信息,请阅读 Blazor University 上的 Multi-threaded rendering 部分。
这里有一些证据:
首先,创建 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 和 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 和 await,然后在最后一次 C# 调用中仅恢复 await,如下所示:
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
注意:您的示例代码会导致多个用户同时执行一个静态方法。在您的场景中,您应该调用实例方法。