在 Blazor 中显示计时器
Display a timer in Blazor
我正在尝试在服务器端 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();
});
}
}
在你的 F# Timer.Elapsed
事件处理程序中,你的最后一行是 timeEvent
(没有参数),我从你的代码的其余部分看到 timeEvent
是一个 Action
已转换为 F# 函数。由于您在 timeEvent
之后没有写入任何参数,该行所做的是将 timeEvent
的 value 指定为事件的 return 值处理程序,即您的事件处理程序正在 returning 一个函数。或者它 将 return 一个函数,如果事件处理程序 return 编辑了 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
)
rmunn 让我走上了正确的轨道,但我的逻辑有点偏离,因为我没有完全理解计时器 class 实际在做什么。我不仅要考虑开始时间,还要考虑间隔时间才能使所有内容同步,现在代码可以按预期工作。这是更新后的代码:
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();
});
}
}
我正在尝试在服务器端 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();
});
}
}
在你的 F# Timer.Elapsed
事件处理程序中,你的最后一行是 timeEvent
(没有参数),我从你的代码的其余部分看到 timeEvent
是一个 Action
已转换为 F# 函数。由于您在 timeEvent
之后没有写入任何参数,该行所做的是将 timeEvent
的 value 指定为事件的 return 值处理程序,即您的事件处理程序正在 returning 一个函数。或者它 将 return 一个函数,如果事件处理程序 return 编辑了 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
)
rmunn 让我走上了正确的轨道,但我的逻辑有点偏离,因为我没有完全理解计时器 class 实际在做什么。我不仅要考虑开始时间,还要考虑间隔时间才能使所有内容同步,现在代码可以按预期工作。这是更新后的代码:
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();
});
}
}