Elixir - การจัดการข้อผิดพลาด

Elixir มีกลไกข้อผิดพลาดสามประการ: ข้อผิดพลาดการขว้างและการออก ให้เราสำรวจกลไกแต่ละอย่างโดยละเอียด

ข้อผิดพลาด

ข้อผิดพลาด (หรือข้อยกเว้น) ใช้เมื่อมีสิ่งพิเศษเกิดขึ้นในโค้ด สามารถเรียกดูข้อผิดพลาดตัวอย่างได้โดยพยายามเพิ่มตัวเลขลงในสตริง -

IO.puts(1 + "Hello")

เมื่อรันโปรแกรมข้างต้นจะเกิดข้อผิดพลาดดังต่อไปนี้ -

** (ArithmeticError) bad argument in arithmetic expression
   :erlang.+(1, "Hello")

นี่คือตัวอย่างข้อผิดพลาดในตัว

เพิ่มข้อผิดพลาด

เราทำได้ raiseข้อผิดพลาดในการใช้ฟังก์ชันเพิ่ม ให้เราพิจารณาตัวอย่างเพื่อให้เข้าใจตรงกัน -

#Runtime Error with just a message
raise "oops"  # ** (RuntimeError) oops

ข้อผิดพลาดอื่น ๆ สามารถเกิดขึ้นได้ด้วยการเพิ่ม / 2 ผ่านชื่อข้อผิดพลาดและรายการอาร์กิวเมนต์คำหลัก

#Other error type with a message
raise ArgumentError, message: "invalid argument foo"

คุณยังสามารถกำหนดข้อผิดพลาดของคุณเองและเพิ่มข้อผิดพลาดเหล่านั้น ลองพิจารณาตัวอย่างต่อไปนี้ -

defmodule MyError do
   defexception message: "default message"
end

raise MyError  # Raises error with default message
raise MyError, message: "custom message"  # Raises error with custom message

การช่วยเหลือข้อผิดพลาด

เราไม่ต้องการให้โปรแกรมของเราหยุดทำงานอย่างกะทันหัน แต่จำเป็นต้องจัดการข้อผิดพลาดอย่างรอบคอบ สำหรับสิ่งนี้เราใช้การจัดการข้อผิดพลาด เราrescue ข้อผิดพลาดโดยใช้ไฟล์ try/rescueสร้าง. ให้เราพิจารณาตัวอย่างต่อไปนี้เพื่อทำความเข้าใจเช่นเดียวกัน -

err = try do
   raise "oops"
rescue
   e in RuntimeError -> e
end

IO.puts(err.message)

เมื่อรันโปรแกรมข้างต้นจะให้ผลลัพธ์ดังนี้ -

oops

เราได้จัดการข้อผิดพลาดในคำสั่งการช่วยเหลือโดยใช้การจับคู่รูปแบบ หากเราไม่ได้ใช้ข้อผิดพลาดใด ๆ และเพียงแค่ต้องการใช้เพื่อจุดประสงค์ในการระบุตัวตนเรายังสามารถใช้แบบฟอร์ม -

err = try do
   1 + "Hello"
rescue
   RuntimeError -> "You've got a runtime error!"
   ArithmeticError -> "You've got a Argument error!"
end

IO.puts(err)

เมื่อรันโปรแกรมด้านบนจะให้ผลลัพธ์ดังนี้ -

You've got a Argument error!

NOTE- ฟังก์ชั่นส่วนใหญ่ในไลบรารีมาตรฐาน Elixir จะถูกใช้สองครั้งเมื่อคืนค่าทูเปิลและเวลาอื่น ๆ เพิ่มข้อผิดพลาด ตัวอย่างเช่นไฟล์File.read และ File.read!ฟังก์ชั่น. อันแรกส่งคืนทูเพิลหากอ่านไฟล์สำเร็จและหากพบข้อผิดพลาดทูเปิลนี้จะถูกใช้เพื่อให้เหตุผลของข้อผิดพลาด ข้อที่สองทำให้เกิดข้อผิดพลาดหากพบข้อผิดพลาด

หากเราใช้แนวทางฟังก์ชันแรกเราจำเป็นต้องใช้ case สำหรับรูปแบบที่ตรงกับข้อผิดพลาดและดำเนินการตามนั้น ในกรณีที่สองเราใช้วิธีการลองช่วยเหลือสำหรับรหัสที่มีแนวโน้มเกิดข้อผิดพลาดและจัดการข้อผิดพลาดตามนั้น

พ่น

ใน Elixir สามารถโยนมูลค่าและจับได้ในภายหลัง Throw and Catch สงวนไว้สำหรับสถานการณ์ที่ไม่สามารถดึงค่าได้เว้นแต่จะใช้ Throw and Catch

อินสแตนซ์ค่อนข้างผิดปกติในทางปฏิบัติยกเว้นเมื่อเชื่อมต่อกับไลบรารี ตัวอย่างเช่นสมมติว่าตอนนี้โมดูล Enum ไม่ได้ให้ API ใด ๆ สำหรับการค้นหาค่าและเราจำเป็นต้องค้นหาตัวคูณ 13 ตัวแรกในรายการตัวเลข -

val = try do
   Enum.each 20..100, fn(x) ->
      if rem(x, 13) == 0, do: throw(x)
   end
   "Got nothing"
catch
   x -> "Got #{x}"
end

IO.puts(val)

เมื่อรันโปรแกรมข้างต้นจะให้ผลลัพธ์ดังนี้ -

Got 26

ออก

เมื่อกระบวนการตายด้วย“ สาเหตุตามธรรมชาติ” (เช่นข้อยกเว้นที่ไม่สามารถจัดการได้) ระบบจะส่งสัญญาณออก กระบวนการสามารถตายได้โดยการส่งสัญญาณออกอย่างชัดเจน ให้เราพิจารณาตัวอย่างต่อไปนี้ -

spawn_link fn -> exit(1) end

ในตัวอย่างข้างต้นกระบวนการที่เชื่อมโยงเสียชีวิตโดยการส่งสัญญาณออกที่มีค่า 1 โปรดทราบว่าทางออกสามารถ "จับ" ได้โดยใช้ try / catch ตัวอย่างเช่น -

val = try do
   exit "I am exiting"
catch
   :exit, _ -> "not really"
end

IO.puts(val)

เมื่อรันโปรแกรมข้างต้นจะให้ผลลัพธ์ดังนี้ -

not really

หลังจาก

บางครั้งจำเป็นต้องตรวจสอบให้แน่ใจว่าทรัพยากรได้รับการล้างข้อมูลหลังจากการดำเนินการบางอย่างที่อาจทำให้เกิดข้อผิดพลาด โครงสร้าง try / after ช่วยให้คุณทำเช่นนั้นได้ ตัวอย่างเช่นเราสามารถเปิดไฟล์และใช้ after clause เพื่อปิดไฟล์ได้แม้ว่าจะมีบางอย่างผิดพลาด

{:ok, file} = File.open "sample", [:utf8, :write]
try do
   IO.write file, "olá"
   raise "oops, something went wrong"
after
   File.close(file)
end

เมื่อเรารันโปรแกรมนี้มันจะทำให้เรามีข้อผิดพลาด แต่after คำสั่งจะทำให้แน่ใจว่า file descriptor ถูกปิดเมื่อเกิดเหตุการณ์ดังกล่าว