我正在尝试在服务器端Blazor应用中显示倒数计时器。我的代码同时在F#和C#中。该代码有些起作用,但是计时器永远不会按预期停止,并且计时器显示不会偶尔呈现所有数字。这是我第一次尝试使用Blazor服务器端应用程序。我不确定问题是异步问题,计时器问题还是渲染问题。
这是我的代码:
F#
let private setTimer countDown timeEvent =
let timer = new Timer(float countDown * float 1000)
let mutable time = 0
time <- countDown
timer.Elapsed.Add(fun arg ->
time <- time - 1
if time = 0
then
timer.Stop()
timer.Dispose()
else
()
timeEvent arg
)
timer.AutoReset <- true
timer.Start()
let setTimerAsync countDown timeEvent = async{
setTimer countDown timeEvent
do! Async.Sleep (countDown * 1000)
}
type Timer (countDown) =
member val CountDown : int = countDown with get,set
member this.SetTimeAsTask (timeEvent) =
setTimerAsync countDown timeEvent |> Async.StartAsTask
C#/开拓者
@page "/CountDown"
@using System.Timers
@using ClientTImer
@using Microsoft.FSharp.Core
<h3>Count Down</h3>
<p>
Task: @task <br />
Status: @status
</p>
<p>
Timer: @time
</p>
@code {
string task = "";
string status = "";
int time = 5;
protected override async Task OnInitializedAsync()
{
// Initial task and status
task = "First Task";
status = "Status One";
Action<System.Timers.ElapsedEventArgs> timeEvent =
t =>
{
UpdateTime().Wait();
};
var func = FuncConvert.ToFSharpFunc(timeEvent);
await new ClientTImer.Timer(time).SetTimeAsTask(func);
// Update task and status
task = "Second Task";
status = "Status Two";
await new ClientTImer.Timer(time).SetTimeAsTask(func);
// Update task and status
task = "Third Task";
status = "Status Three";
}
public async Task UpdateTime()
{
await InvokeAsync(() =>
{
time--;
StateHasChanged();
});
}
}
最佳答案
在F#Timer.Elapsed
事件处理程序中,最后一行是timeEvent
(无参数),从其余代码中我看到timeEvent
是已转换为F#函数的Action
。由于您没有在timeEvent
之后编写任何参数,因此该行正在将timeEvent
的值指定为事件处理程序的返回值,即您的事件处理程序正在返回一个函数。如果事件处理程序返回的不是void
(或F#术语中的unit
),则它将返回一个函数。既然他们没有,我怀疑您在timeEvent
行上收到了一条警告,内容是固有地忽略了timeEvent
的值。
另外,您在F#中的timer.Elapsed.Add
行对我来说似乎是错误的。事件上的Add
方法采用类型为'T -> unit
的参数,其中'T
是事件为您提供的任何数据类型:对于计时器上的Elapsed
事件,这将是ElapsedEventArgs
实例。您应该传递给Add
的是fun elapsedEventArgs -> ...
。然后,您将更改timeEvent
行,以实际为它传递一个参数(与elapsedEventArgs
相同),以便调用它并实际执行某些操作。
另外,每当您将数字减一并将其与0进行比较时,我总是喜欢将其作为<=
而不是=
进行比较,但很可能我稍后会以可能导致减量两次发生的方式更改代码。如果我的比较是= 0
,并且双倍减数将数字从1改为-1,则if x = 0
分支不会触发。但是,如果我将其与<= 0
进行比较,那么即使我在其他地方犯了错误,它也会触发。所以我建议写if time <= 0
而不是if time = 0
。
换句话说,我认为您的timer.Elapsed
事件处理程序需要如下所示:
timer.Elapsed.Add(fun evtArgs ->
time <- time - 1
if time <= 0
then
timer.Stop()
timer.Dispose()
else
()
timeEvent evtArgs
)