我正在尝试在服务器端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
)

10-06 01:21