17-異常處理
17-異常處理
Errors
Throws
Exits
After
變數作用域
Elixir有三種錯誤處理機制:errors,throws和exits。本章我們將逐個講解它們,包括應該在何時使用哪一個。
17.1-Errors
舉個例子,嘗試讓原子加上一個數字,就會激發一個錯誤(errors):
iex> :foo + 1
** (ArithmeticError) bad argument in arithmetic expression
:erlang.+(:foo, 1)
使用巨集raise/1
可以在任何時候激發一個執行時錯誤:
iex> raise "oops"
** (RuntimeError) oops
用raise/2
,並且附上錯誤名稱和一個鍵值列表可以激發規定好的錯誤:
iex> raise ArgumentError, message: "invalid argument foo"
** (ArgumentError) invalid argument foo
你可以使用defexception/2
定義你自己的錯誤。最常見的是定義一個有訊息說明的錯誤:
iex> defexception MyError, message: "default message"
iex> raise MyError
** (MyError) default message
iex> raise MyError, message: "custom message"
** (MyError) custom message
用try/catch
結構可以處理異常:
iex> try do
...> raise "oops"
...> rescue
...> e in RuntimeError -> e
...> end
RuntimeError[message: "oops"]
這個例子處理了一個執行時異常,返回該錯誤本身(會被顯示在IEx對話中)。
在實際操作中,Elixir程式設計師很少使用try/rescue
結構。
例如,當檔案開啟失敗,很多程式語言會強制你去處理一個異常。而Elixir提供的File.read/1
函式返回包含資訊的元組,不管檔案開啟成功與否:
iex> File.read "hello"
{:error, :enoent}
iex> File.write "hello", "world"
:ok
iex> File.read "hello"
{:ok, "world"}
這個例子中沒有try/rescue
。如果你想處理開啟檔案可能的不同結果,你可以使用case來匹配:
iex> case File.read "hello" do
...> {:ok, body} -> IO.puts "got ok"
...> {:error, body} -> IO.puts "got error"
...> end
使用這個匹配處理,你可以自己決定要不要把問題丟擲來。
這就是為什麼Elixir不讓File.read/1
等函式自己丟擲異常。它把決定權留給程式設計師,讓他們尋找最合適的處理方法。
如果你真的期待檔案存在(開啟檔案時檔案不存在這確實是一個錯誤),你可以簡單地使用File.read!/1
:
iex> File.read! "unknown"
** (File.Error) could not read file unknown: no such file or directory
(elixir) lib/file.ex:305: File.read!/1
換句話說,我們避免使用try/rescue
是因為我們不用錯誤處理來控制程式執行流程。
在Elixir中,我們視錯誤為其字面意思:它們只不過是用來表示意外或異常的資訊。
如果你真的希望改變執行過程,你可以使用throws
。
17.2-Throws
在Elixir中,你可以丟擲(throw)一個值稍後處理。throw
和catch
就被保留著為了處理一些你丟擲了值,但是不用try/catch
就取不到的情況。
這些情況實際中很少出現,除非當一個庫的介面沒有提供合適的API等情況。 例如,假如列舉模組沒有提供任何API來尋找某範圍內第一個13的倍數:
iex> try do
...> Enum.each -50..50, fn(x) ->
...> if rem(x, 13) == 0, do: throw(x)
...> end
...> "Got nothing"
...> catch
...> x -> "Got #{x}"
...> end
"Got -39"
但是它提供了這樣的函式Enum.find/2
:
iex> Enum.find -50..50, &(rem(&1, 13) == 0)
-39
17.3-Exits
每段Elixir程式碼都在程式中執行,程式與程式相互交流。當一個程式終止了,它會發出exit
訊號。
一個程式可以通過顯式地發出這個訊號來終止:
iex> spawn_link fn -> exit(1) end
#PID<0.56.0>
** (EXIT from #PID<0.56.0>) 1
上面的例子中,連結著的程式通過傳送exit
訊號(帶有引數數字1)而終止。Elixir shell自動處理這個資訊並把它們顯示在終端上。
exit還可以被try/catch
塊捕獲處理:
iex> try do
...> exit "I am exiting"
...> catch
...> :exit, _ -> "not really"
...> end
"not really"
因為try/catch
已經很少用了,用它們捕獲exit訊號就更少見了。
exit訊號是Erlang虛擬機器提供的高容錯性的重要部分。程式通常都在監督樹(supervision trees)下執行。 監督樹本身也是程式,它們通過exit訊號監督其它程式。然後通過某些策略決定是否重啟。
就是這種監督系統使得try/catch
和try/rescue
程式碼塊很少用到。與其處理一個錯誤,不如讓它快速失敗。
因為在失敗後,監督樹會保證我們的程式將恢復到一個已知的初始狀態去。
17.4-After
有時候有必要使用try/after
來保證某資源在使用後被正確關閉或清除。
例如,我們開啟一個檔案,然後使用try/after
來確保它在使用後被關閉:
iex> {:ok, file} = File.open "sample", [:utf8, :write]
iex> try do
...> IO.write file, "olá"
...> raise "oops, something went wrong"
...> after
...> File.close(file)
...> end
** (RuntimeError) oops, something went wrong
17.5-變數作用域
對於定義在try/catch/rescue/after
程式碼塊中的變數,切記不可讓它們洩露到外面去。這時因為try
程式碼塊有可能會失敗,而這些變數此時並沒有正常繫結數值:
iex> try do
...> from_try = true
...> after
...> from_after = true
...> end
iex> from_try
** (RuntimeError) undefined function: from_try/0
iex> from_after
** (RuntimeError) undefined function: from_after/0
至此我們結束了對try/catch/rescue
等知識的介紹。你會發現其實這些概念在實際的Elixir程式設計中不太常用。儘管的確有時也會用到。
是時候討論一些Elixir的概念,如列表速構(comprehensions)和魔法印(sigils)了。
相關文章
- 異常篇——異常處理
- 異常處理
- 異常-throws的方式處理異常
- 異常處理與異常函式函式
- JavaScript 異常處理JavaScript
- ThinkPHP 異常處理PHP
- React 異常處理React
- 08、異常處理
- JAVA 異常處理Java
- JAVA異常處理Java
- Abp 異常處理
- oracle異常處理Oracle
- PowerShell 異常處理
- plsql異常處理SQL
- Swift 異常處理Swift
- JS異常處理JS
- app異常處理APP
- Oracle 處理異常Oracle
- MySQL異常處理MySql
- 異常處理 (轉)
- 異常的處理
- Java 異常處理Java
- golang - 異常處理Golang
- 異常處理2
- 異常處理1
- 異常處理機制(二)之異常處理與捕獲
- JSP 異常處理如何處理?JS
- Java 異常表與異常處理原理Java
- restframework 異常處理及自定義異常RESTFramework
- windows核心程式設計---未處理異常,向量化異常處理與C++異常Windows程式設計C++
- Python異常處理Python
- PHP 核心 - 異常處理PHP
- Python——異常處理Python
- JAVA_異常處理Java
- SpringMVC異常處理SpringMVC
- 異常處理過程
- C++ 異常處理C++
- PL SQL異常處理.SQL