Показать таймер в Blazor - PullRequest
       86

Показать таймер в Blazor

2 голосов
/ 29 сентября 2019

Я пытаюсь отобразить таймер обратного отсчета в приложении 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 # / Blazor

@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();
        });
    }

}

Ответы [ 2 ]

1 голос
/ 30 сентября 2019

rmunn поставил меня на правильный путь, но моя логика была немного не в порядке, так как я не до конца понимал, что на самом деле делал класс таймера.Мне нужно было учитывать не только время начала, но и интервал, чтобы все было синхронизировано, и теперь код работает так, как задумано.Вот обновленный код:

F #:

let private setTimer countDownSec intervalSec timeEvent =

    let timer = new Timer(float intervalSec * float 1000)
    let mutable time = 0

    time <- countDownSec

    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 internal setTimerAsync countDownSec intervalSec timeEvent = async{
    setTimer countDownSec intervalSec timeEvent
    do! Async.Sleep (countDownSec * 1000)
}

type Timer (countDown) =

    member val CountDown : int = countDown with get,set

    member this.SetTimeAsTask (timeEvent, interval) =
        setTimerAsync countDown interval timeEvent |> Async.StartAsTask

C # / Blazor:

@page "/CountDown"
@using System.Timers
@using ClientTImer
@using Microsoft.FSharp.Core
@using System.Threading

<h3>Count Down</h3>
<p>
    Task: @task <br />
    Status: @status
</p>
<p>
    Timer: @time
</p>

@code {
    string task = "";
    string status = "";
    int startCount = 5;
    int time;

    protected override async Task OnInitializedAsync()
    {
        time = startCount;

        // 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(startCount).SetTimeAsTask(func,1);

        // Update task and status
        task = "Second Task";
        status = "Status Two";
        await new ClientTImer.Timer(startCount).SetTimeAsTask(func,1);

        // Update task and status
        task = "Third Task";
        status = "Status Three";

    }

    public async Task UpdateTime()
    {
        await InvokeAsync(() =>
        {
            time--;

            if(time <= 0)
            {
                time = startCount;
            }

            StateHasChanged();
        });
    }

}
1 голос
/ 29 сентября 2019

Внутри вашего обработчика событий F # Timer.Elapsed ваша последняя строка равна timeEvent (без параметров), и из остального вашего кода я вижу, что timeEvent - это Action, который был преобразован в функцию F #,Поскольку вы не записали никаких параметров после timeEvent, то, что делает эта строка, указывает значение из timeEvent в качестве возвращаемого значения обработчика события, т.е. ваш обработчик события возвращает функцию.Или вернет функцию, если обработчики событий вернут что-то отличное от void (или unit в терминах F #).Поскольку они не подозревают, что у вас есть предупреждение в этой строке timeEvent, которое говорит о том, что значение timeEvent по своей сути игнорируется.

Кроме того, ваша строка timer.Elapsed.Add в F # выглядитнеправильно со мной.Метод 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
)
...