Fork me on GitHub

Διαχείριση Σφαλμάτων

Παρόλο που είναι πιο συνηθισμένη η επιστροφή της τούπλας {:error, reason}, η Elixir υποστηρίζει εξαιρέσεις και σε αυτό το μάθημα θα δούμε πως να χειριζόμαστε σφάλματα και τους διαφορετικούς μηχανισμούς που μας είναι διαθέσιμοι.

Γενικά η σύμβαση στην Elixir είναι να δημιουργείτε μια συνάρτηση (example/1) η οποία επιστρέφει {:ok, result} και {:error, reason} και μια ξεχωριστή συνάρτηση (example/1) που επιστρέφει το “σκέτο” result ή σηκώνει ένα σφάλμα.

Αυτό το μάθημα θα εστιάσει στην αλληλεπίδραση με την τελευταία.

Πίνακας περιεχομένων

Διαχείριση Σφαλμάτων

Πριν μπορέσουμε να διαχειριστούμε τα σφάλματα χρειάζεται να τα δημιουργήσουμε και ο πιο απλός τρόπος να το κάνουμε είναι με την raise/1:

iex> raise "Ωχ όχι!"
** (RuntimeError) Ωχ όχι!

Αν θέλουμε να ορίσουμε τον τύπο και το μήνυμα, χρειάζεται η χρήση της raise/2:

iex> raise ArgumentError, message: "Ο τύπος ορίσματος είναι άκυρος"
** (ArgumentError) Ο τύπος ορίσματος είναι άκυρος

Όταν ξέρουμε ότι ένα σφάλμα μπορεί να προκύψει, θα το χειριστούμε με τη χρήση των try/rescue και την αντιπαραβολή προτύπων:

iex> try do
...>   raise "Ωχ όχι!"
...> rescue
...>   e in RuntimeError -> IO.puts("Ένα σφάλμα προέκυψε: " <> e.message)
...> end
Ένα σφάλμα προέκυψε: Ωχ όχι!
:ok

Είναι δυνατό να αντιπαραβάλουμε πολλαπλά σφάλματα σε μια και μοναδική rescue:

try do
  opts
  |> Keyword.fetch!(:source_file)
  |> File.read!
rescue
  e in KeyError -> IO.puts "Λείπει η επιλογή :source_file"
  e in File.Error -> IO.puts "Αδύνατη η ανάγνωση από το πηγαίο αρχείο"
end

After

Κάποιες φορές μπορεί να είναι απαραίτητο να γίνουν κάποιες ενέργειες μετά τις try/rescue, άσχετα με το σφάλμα. Για αυτό έχουμε την try/after. Αν είστε εξοικειομένοι με την Ruby, αυτή είναι όμοια με την begin/rescue/ensure ή την try/catch/finally στη Java:

iex> try do
...>   raise "Ώχ όχι!"
...> rescue
...>   e in RuntimeError -> IO.puts("Ένα σφάλμα προέκυψε: " <> e.message)
...> after
...>   IO.puts "Το τέλος!"
...> end
An error occurred: Ώχ όχι!
Το τέλος!
:ok

Είναι πολύ συχνή η χρήση της με αρχεία η συνδέσεις που πρέπει να κλείσουν:

{:ok, file} = File.open "example.json"
try do
   # Κάντε κάτι καταστροφικό
after
   File.close(file)
end

Νέα Σφάλματα

Παρόλο που η Elixir περιλαμβάνει έναν αριθμό προκαθορισμένων τύπων λαθών όπως το RuntimeError, διατηρούμε τη δυνατότητα να δημιουργήσουμε τα δικά μας αν χρειαζόμαστε κάτι συγκεκριμμένο. Η δημιουργία ενός νέου σφάλματος είναι έυκολη με την χρήση της μακροεντολής defexception/1 η οποία βολικά δέχεται την επιλογή :message σαν το προκαθορισμένο μήνυμα σφαλμάτων:

defmodule ExampleError do
  defexception message: "προέκυψε ένα παραδειγματικό σφάλμα"
end

Ας δοκιμάσουμε το νέο μας σφάλμα:

iex> try do
...>   raise ExampleError
...> rescue
...>   e in ExampleError -> e
...> end
%ExampleError{message: "προέκυψε ένα παραδειγματικό σφάλμα"}

Ρίψεις

Ένας άλλος μηχανισμός για την εργασία με σφάλματα στην Elixir είναι οι throw και catch. Στην πράξη, αυτές προκύπτουν πολύ σπάνια σε νεότερο κώδικα Elixir αλλά είναι σημαντικό να τις ξέρουμε και να τις κατανοούμε πάραυτα.

Η συνάρτηση throw/1 δίνει τη δυνατότητα να βγούμε από την εκτέλεση με μια συγκεκριμμένη τιμή την οποία μπορούμε να πίασουμε (catch) και χρησιμοποιήσουμε:

iex> try do
...>   for x <- 0..10 do
...>     if x == 5, do: throw(x)
...>     IO.puts(x)
...>   end
...> catch
...>   x -> "Πιάστηκε: #{x}"
...> end
0
1
2
3
4
"Πιάστηκε: 5"

Όπως αναφέρθηκε, οι throw/catch είναι αρκετά σπάνιες και τυπικά υπάρχουν σαν προσωρινές λύσεις όταν βιβλιοθήκες αποτυγχάνουν να παρέχουν επαρκή API.

Έξοδος

Ο τελευταίος μηχανισμός που μας παρέχει η Elixir είναι η exit. Τα σήματα εξόδου προκύπτουν όταν μια διεργασία πεθαίνει και είναι σημαντικό μέρος της ανοχής σφαλμάτων της Elixir.

Για να βγούμε με σαφήνεια χρησιμοποιούμε την exit/1:

iex> spawn_link fn -> exit("oh no") end
** (EXIT from #PID<0.101.0>) "oh no"

Παρόλο που είναι πιθανόν να πιάσουμε την έξοδο με τις try/catch, αυτό είναι εξαιρετικά σπάνιο. Σχεδόν σε όλες τις περιπτώσεις είναι επωφελές να αφήσουμε τον διαχειριστή να χειριστεί την έξοδο της διεργασίας:

iex> try do
...>   exit "Ώχ όχι!"
...> catch
...>   :exit, _ -> "μπλοκαρίστηκε η έξοδος"
...> end
"μπλοκαρίστηκε η έξοδος"

Share This Page