鉴于
我有一个onscroll或mousemove javascript事件处理程序,它在服务器上调用C#方法:
之后
服务器上是否可以保证C#方法调用的顺序?
例如以下javascript:
document.body.addEventListener("scroll",(e) => {
DotNet.invokeMethodAsync("BlazorSample", "HandleOnScroll", e)
});
和C#
@code {
[JSInvokable()]
public static async Task HandleOnScroll()
{
// ...
}
}
一个类似的问题将反过来,从DotNet调用到JS。
答案 0 :(得分:3)
简短答案:是。
更长的答案:
从C#到JavaScript的调用,反之亦然,立即执行。
然后,由Render Dispatcher处理对C#的调用,这意味着,如果C#代码是同步的(或与异步代码一直等待下去),则每次对C#的调用将在下一个调用开始之前完成。 / p>
如果C#代码是异步的并且不等待异步操作(例如Task.Delay
),则Render Dispatcher将在当前方法等待时立即开始运行下一个调用的方法。
虽然多个线程不会并发运行。直到当前分派的任务执行await
或完成后,await
之后,渲染分派器才会继续执行代码。
有效地,Render Dispatcher序列化对组件的访问,因此一次在任何组件上都只运行一个线程-不管它们上运行了多少线程。
PS:对于外部刺激触发的任何代码(例如,由Singleton服务上的另一个用户线程引发的事件),您都可以使用InvokeAsync(......)
进行同样的操作。
如果您想了解有关渲染器工作原理的更多信息,请阅读Blazor大学的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
注意:您的示例代码将导致多个用户同时执行静态方法。在您的方案中,您应该调用一个实例方法。