我怎样才能在elixir中实现延迟模式?
让我解释一下它是什么。假设我有一些fn()
,应该在此之后延迟n
秒后执行。但是如果我第二次调用fn()
这个函数应该在第二次调用后的n
秒内实现,依此类推。应该有一种方法来退出这个功能评估。
您可以查看Lodash的_.debounce函数作为参考。
答案 0 :(得分:2)
一个非常天真和简单的解决方案可以使用原始流程。
defmodule Debounce do
def start(fun, timeout) do
ref = make_ref()
# this function is invoked when we wait for a next application
recur = fn recur, run ->
receive do
^ref ->
# let's start counting!
run.(recur, run)
end
end
# this function is invoked when we "swallow" next applications
# and wait until we finally apply the function
run = fn recur, run ->
receive do
^ref ->
# let's reset the counter
run.(recur, run)
after
timeout ->
# time is up, let's call it for real & return to waiting
fun.()
recur.(recur, run)
end
end
pid = spawn_link(fn -> recur.(recur, run) end)
fn -> send(pid, ref) end
end
end
让我们看一个例子
iex> f = Debounce.start(fn -> IO.puts("Hello"), 5000)
iex> f.()
iex> f.()
# wait some time
Hello
iex> f.() # wait some time
Hello
然而,这有很多问题 - 我们的“去抖动”过程有效地永远存在,我们不能取消去抖动,可靠性充其量也是粗略的。我们可以改进,但是我们会放弃一个我们可以调用的简单乐趣的返回值,而是我们需要调用一个特殊的函数来“应用”我们的去抖动。
defmodule Debounce do
def start(fun, timeout) do
ref = make_ref()
# this function is invoked when we wait for a next application
recur = fn recur, run ->
receive do
{^ref, :run} ->
# let's start counting!
run.(recur, run)
{^ref, :cancel} ->
:cancelled
end
end
# this function is invoked when we "swallow" next applications
# and wait until we finally apply the function
run = fn recur, run ->
receive do
{^ref, :run} ->
# let's reset the counter
run.(recur, run)
{^ref, :cancel} ->
:cancelled
after
timeout ->
# time is up, let's call it for real & return to waiting
fun.()
recur.(recur, run)
end
end
pid = spawn_link(fn -> recur.(recur, run) end)
{pid, ref}
end
def apply({pid, ref}) do
send(pid, {ref, :run})
end
def cancel({pid, ref}) do
send(pid, {ref, :cancel})
end
end
让我们看一个例子:
iex> deb = Debounce.start(fn -> IO.puts("Hello"), 5000)
iex> Debounce.apply(deb)
iex> Debounce.apply(deb)
# wait some time
Hello
iex> Debounce.apply(deb)
iex> Debounce.cancel(deb)
# wait some time
# nothing
这仍有一些可能的极端情况 - 生产版本可能会使用Task或GenServer。
答案 1 :(得分:0)
为了存储状态,您需要一个进程;一个简单的功能还不够。为此创建流程只需几行代码:
defmodule Debounce do
def start_link(f, timeout) do
spawn_link(__MODULE__, :loop, [f, timeout])
end
def loop(f, timeout) do
receive do
:bounce -> loop(f, timeout)
:exit -> :ok
after timeout ->
f.()
end
end
end
您可以发送此流程:bounce
,并将其超时重置为Debounce.start_link/2
中指定的超时。您也可以发送此流程:exit
,并在不运行该功能的情况下自行退出。
测试:
f = Debounce.start_link(fn -> IO.inspect(:executing) end, 1000)
IO.puts 1
send f, :bounce
:timer.sleep(500)
IO.puts 2
send f, :bounce
:timer.sleep(500)
IO.puts 3
send f, :bounce
:timer.sleep(500)
IO.puts 4
send f, :bounce
:timer.sleep(2000)
IO.puts 5
输出:
1
2
3
4
:executing
5
答案 2 :(得分:-1)
好的,这是一个简化的案例,让你前进:
此处n
在几秒钟内不,但是循环步骤,因此您需要大n
个来查看任何延迟。这里我使用IO.puts
作为调用函数的示例。
defmodule myModule do
def loop(list,count) do
receive do
n -> list = list ++ n
:die ->
Process.exit(self(), :kill )
end
if count == 0 do
IO.puts( "timeout" )
[head|tail] = list
loop(tail, head)
else
loop(list, count-1)
end
end
end