2017-01-09 4 views
0

엘릭서에 지연 패턴을 어떻게 구현할 수 있습니까?엘릭서에 지연 패턴을 구현하는 방법은 무엇입니까?

설명해 드리겠습니다. 내가 fn()을 가지고 있다고 가정 해보자. 그러면 n 초 후에 지연되어야한다. 하지만이 전화를 fn() 두 번째로 호출하면이 함수는 두 번째 호출 후 n 초 내에 구현되어야합니다. 이 함수 평가를 종료하는 방법도 있어야합니다.

Lodash의 _.debounce 기능을 참조 할 수 있습니다. 당신이 지연을보고 큰 n 년대를 필요로하기 위하여려고하고있다, 그래서 여기 n 초 만 루프 단계 하지입니다 : 여기에 좋아

+1

내 머리 꼭대기에서 간단한 genserver로 구현할 수 있습니다. 어쩌면 간단한 방법이 있습니다. – JustMichael

+0

그래, 그래,하지만 더 단순한 추상화를 찾고있다 – asiniy

+0

프로세스를 원하는 기능을 원하지 않는다. http://elixir-lang.org/getting-started/processes.html 프로세스가 현재 값을 카운팅하는 동안 루프는 n의 새로운 수신 값을받습니다.이 값은 로컬 상태로 유지되는 목록에 저장됩니다. 각 카운트 다운이 끝나면 목록의 다음 멤버에게 가서 다시 카운트 다운을합니다. – GavinBrelstaff

답변

-1

당신이 가야하는 단순화 된 경우입니다. 여기서는 함수를 호출하는 예제로 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 
+0

OP는 어떤 방법으로 요청 했습니까? 질문은 간단한 지연에 관한 것이 아닙니다. – michalmuskala

+0

@michalmuskala 각 지연 시간 초과 후 함수의 예로 IO.puts를 호출합니다. 물론 프로세스를 생성 한 다음 원하는 경우 n 값의 pid 메시지를 보내야합니다. – GavinBrelstaff

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

아주 순진하고 단순한 솔루션은 원시 프로세스를 사용할 수 있습니다.

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 

이 여전히 가능 코너 케이스를 가지고 - 생산 버전은 아마 작업 또는 GenServer을 사용합니다.