Search code examples
elixirblockinggen-server

Elixir blocked GenServer process


I have a simple GenServer within which I wish to create a loop that calls a function every two seconds:

defmodule MyModule do
  use GenServer

  def start_link(time) do
    GenServer.start_link(__MODULE__,time)
  end

  #Start loop
  def init(time) do
    {:ok, myLoop(time)}
  end

  #Loop every two seconds
  def myLoop(time) do

    foo = bah(:someOtherProcess, {time})
    IO.puts("The function value was: #{foo}")
    :timer.sleep(2000)
    myLoop(time + 2)
  end
end 

But when I call with:

{:ok, myServer} =MyModule.start_link(time)
IO.puts("Now I can carry on...")

I never see a return from the above call. This is kind of obvious I guess. So my question is, how can I create the loop I'd like without blocking the process from downstream execution tasks?

Thanks.


Solution

  • The best/cleanest way to accomplish what you are trying to do is with Process.send_after/3. It delegates the timeout to the scheduler, not another process.

    defmodule MyModule do
      use GenServer
    
      def start_link(time) do
        GenServer.start_link(__MODULE__,time)
      end
    
      def init(time) do
        schedule_do_something(time)
        {:ok, %{time: time}}
      end
    
      def handle_info(:do_something, state) do
        %{time: time} = state
        # do something interesting here
        schedule_do_something(time)
        {:noreply, state}
      end
    
      defp schedule_do_something(time_in_seconds) do
        Process.send_after(self, :do_something, (time_in_seconds * 1000))
      end
    end
    

    A GenServer acts like an event loop anyway, so why reimplement this yourself?