Just started learning about OTP. Say if i have a GenServer that timesout
@impl true
def handle_call(:state, _from, state) do
Process.sleep(6000)
{:reply, state, state}
end
I want to try to capture the fact that it timesout and retry the function again. Somehow the rescue block is not triggered. May i know what am i understanding wrongly here?
def state(calculator) do
try do
GenServer.call(calculator, :state)
rescue
e ->
IO.puts("Fail state because: #{e}")
state(calculator)
end
end
def state(calculator) do
try do
GenServer.call(calculator, :state)
catch
:exit, _ ->
IO.puts("there was an error")
e ->
IO.puts("Fail state because: #{e}")
state(calculator)
end
end
First catch clause works but not the other which puzzles me as i thought e -> ... should be match for everything. Can someone explain to me what’s happening here? Also in future how can i tell what kind of catch clause i should use in some other scenarios.
creating | handling with | where y is | and z is
-----------------------------------------------------------------
raise x | catch y, z | :error | %RuntimeError{message: x}
error(x) | catch y, z | :error | x
throw x | catch y, z | :throw | x
exit(x) | catch y, z | :exit | x
It’s because Elixir has three types of error mechanisms: errors (raise/1, raise/2), throws and exits. Generally, when you use try/catch, then a type of error is implicitly set to throw. Here is an example:
try do
throw(:value)
catch
e -> IO.inspect(e)
end
# This is "expanded" to the following code:
try do
throw(:value)
catch
:throw, e -> IO.inspect(e)
end
So if you want to catch exits, then you have to explicitly define this in your expression like this: catch :exit, e -> .... In your code e -> ... is expanded to :throw, e -> ... so it can catch only throws, other kinds of errors will be ignored.
I recommend to check the following links (first one should be enough to master this topic):