Do you want to pick up from where you left of?
Take me there

Penanganan Error

Walaupun lebih umum menggunakan pengembalian tuple {:error, reason}, Elixir mendukung exception dan dalam pelajaran ini kita akan melihat bagaimana menangani error dan berbagai mekanisme yang tersedia untuk kita.

Secara umum, konvensi dalam Elixir adalah untuk membuat sebuah fungsi (example/1) yang mengembalikan {:ok, result} dan {:error, reason} dan fungsi lain yang terpisah (example!/1) yang mengembalikan result saja atau memunculkan (raise) sebuah error.

Pelajaran ini akan fokus pada berinteraksi dengan yang terakhir

Penanganan Error

Sebelum kita dapat menangani error kita perlu membuatnya dan cara termudah adalah dengan raise/1:

iex> raise "Oh no!"
** (RuntimeError) Oh no!

Jika kita ingin menspesifikasikan tipe dan pesan kesalahan (message), kita perlu menggunakan raise/2:

iex> raise ArgumentError, message: "the argument value is invalid"
** (ArgumentError) the argument value is invalid

Ketika kita tahu sebuah error bisa muncul, kita bisa menanganinya menggunakan try/rescue dan pencocokan pola:

iex> try do
...>   raise "Oh no!"
...> rescue
...>   e in RuntimeError -> IO.puts("An error occurred: " <> e.message)
...> end
An error occurred: Oh no!
:ok

Adalah mungkin mencocokkan banyak error dalam satu rescue tunggal:

try do
  opts
  |> Keyword.fetch!(:source_file)
  |> File.read!()
rescue
  e in KeyError -> IO.puts("missing :source_file option")
  e in File.Error -> IO.puts("unable to read source file")
end

After

Pada berbagai kesempatan mungkin perlu melakukan beberapa tindakan setelah try/rescue kita apapun errornya. Untuk ini kita punya try/after. Jika anda familiar dengan Ruby ini seperti begin/rescue/ensure atau di Java try/catch/finally:

iex> try do
...>   raise "Oh no!"
...> rescue
...>   e in RuntimeError -> IO.puts("An error occurred: " <> e.message)
...> after
...>   IO.puts "The end!"
...> end
An error occurred: Oh no!
The end!
:ok

Ini paling sering dipakai dengan file atau koneksi yang harus ditutup:

{:ok, file} = File.open "example.json"
try do
   # Do hazardous work
after
   File.close(file)
end

Error Baru

Sementara Elixir mencakup sejumlah tipe error yang built in seperti RuntimeError kita punya kemampuan membuat tipe error sendiri jika kita perlu sesuatu yang spesifik. Membuat error baru adalah mudah dengan macro defexception/1 yang menerima opsi :message untuk menset pesan kesalahan default:

defmodule ExampleError do
  defexception message: "an example error has occurred"
end

Mari coba pakai error baru kita:

iex> try do
...>   raise ExampleError
...> rescue
...>   e in ExampleError -> e
...> end
%ExampleError{message: "an example error has occurred"}

Throw

Mekanisme lain untuk bekerja dengan error dalam Elixir adalah throw and catch. Dalam prakteknya ini sangat jarang muncul dalam code Elixir yang lebih baru tetapi tetap penting untuk diketahui dan dipahami.

Fungsi throw/1 memberi kita kemampuan untuk keluar dari eksekusi dengan value spesifik yang bisa kita catch (tangkap) dan gunakan:

iex> try do
...>   for x <- 0..10 do
...>     if x == 5, do: throw(x)
...>     IO.puts(x)
...>   end
...> catch
...>   x -> "Caught: #{x}"
...> end
0
1
2
3
4
"Caught: 5"

Sebagaimana disinggung, throw/catch cukup jarang ada dan biasanya hadir sebagai penghadang (stopgap) ketika librari gagal menyediakan API yang memadai.

Exit

Mekanisme kesalahan Elixir yang terakhir adalah exit. Signal exit muncul manakala sebuah proses mati dan merupakan bagian penting dalam toleransi kegagalan Elixir.

Untuk keluar secara eksplisit kita bisa gunakan exit/1:

iex> spawn_link fn -> exit("oh no") end
** (EXIT from #PID<0.101.0>) evaluator process exited with reason: "oh no"

Walau adalah mungkin menangkap sebuah exit dengan try/catch, melakukannya adalah sangat jarang. Dalam hampir semua kasus lebih baik membiarkan supervisor menangani exit proses tersebut:

iex> try do
...>   exit "oh no!"
...> catch
...>   :exit, _ -> "exit blocked"
...> end
"exit blocked"
Caught a mistake or want to contribute to the lesson? Edit this lesson on GitHub!