Skip to content

Error Handling (การจัดการข้อผิดพลาด)

การจัดการข้อผิดพลาดเป็นส่วนสำคัญในการเขียนโปรแกรมที่ช่วยให้แอปพลิเคชันทำงานได้อย่างมีเสถียรภาพและง่ายต่อการดีบัก

Basic Error Handling (การจัดการข้อผิดพลาดพื้นฐาน)

js
// Try-Catch-Finally
// ใช้ try-catch-finally สำหรับจัดการข้อผิดพลาด
function divide(a, b) {
  try {
    if (b === 0) {
      throw new Error("หารด้วยศูนย์ไม่ได้");
    }
    return a / b;
  } catch (error) {
    console.error("เกิดข้อผิดพลาด:", error.message);
    return null;
  } finally {
    console.log("ทำการคำนวณเสร็จสิ้น");
  }
}

// เรียกใช้งาน
divide(10, 2); // 5
divide(10, 0); // แสดงข้อความผิดพลาด

// Promise-based error handling
async function fetchData() {
  try {
    const response = await fetch("https://api.example.com/data");
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error("ไม่สามารถดึงข้อมูลได้:", error);
  }
}
rust
// Rust ใช้ Result และ Option สำหรับจัดการข้อผิดพลาด
fn divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        return Err("หารด้วยศูนย์ไม่ได้".to_string());
    }
    Ok(a / b)
}

// ใช้งาน Result กับ match
match divide(10.0, 2.0) {
    Ok(result) => println!("ผลลัพธ์: {}", result),
    Err(e) => eprintln!("ข้อผิดพลาด: {}", e),
}

// ใช้ unwrap_or_else สำหรับค่าเริ่มต้นเมื่อเกิดข้อผิดพลาด
let result = divide(10.0, 0.0).unwrap_or_else(|e| {
    eprintln!("เกิดข้อผิดพลาด: {}", e);
    0.0 // ค่าเริ่มต้น
});

// Custom error type
#[derive(Debug)]
pub enum MyError {
    DivisionByZero,
    NegativeNumber(i32),
}

impl std::fmt::Display for MyError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            MyError::DivisionByZero => write!(f, "หารด้วยศูนย์ไม่ได้"),
            MyError::NegativeNumber(n) => write!(f, "เลข {} เป็นค่าลบ", n),
        }
    }
}

impl std::error::Error for MyError {}

fn process_number(n: i32) -> Result<f64, MyError> {
    if n < 0 {
        return Err(MyError::NegativeNumber(n));
    }
    // การประมวลผลอื่นๆ
    Ok(n as f64)
}
python
# Basic try-except
# ใช้ try-except-else-finally สำหรับจัดการข้อผิดพลาด
def divide(a: float, b: float) -> float | None:
    try:
        if b == 0:
            raise ValueError("หารด้วยศูนย์ไม่ได้")
        return a / b
    except ValueError as e:
        print(f"ข้อผิดพลาด: {e}")
        return None
    except Exception as e:
        print(f"เกิดข้อผิดพลาดที่ไม่คาดคิด: {e}")
        return None
    else:
        print("คำนวณสำเร็จ")
    finally:
        print("สิ้นสุดการทำงาน")

# เรียกใช้งาน
result = divide(10, 2)  # 5.0
result = divide(10, 0)  # แสดงข้อความผิดพลาด

# Custom exception
class ValidationError(Exception):
    def __init__(self, message="ข้อมูลไม่ถูกต้อง"):
        self.message = message
        super().__init__(self.message)

def validate_age(age: int):
    if age < 0:
        raise ValidationError("อายุต้องมากกว่าหรือเท่ากับ 0")
    if age > 120:
        raise ValidationError("อายุต้องไม่เกิน 120 ปี")
    return True

try:
    validate_age(-5)
except ValidationError as e:
    print(f"ตรวจสอบข้อมูลไม่ผ่าน: {e}")
go
// Go ใช้ error type และ multiple return values
// ฟังก์ชันที่อาจเกิดข้อผิดพลาดจะคืนค่า error เป็นค่าสุดท้าย
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("หารด้วยศูนย์ไม่ได้")
    }
    return a / b, nil
}

// เรียกใช้งาน
result, err := divide(10, 2)
if err != nil {
    log.Fatalf("เกิดข้อผิดพลาด: %v", err)
}
fmt.Printf("ผลลัพธ์: %v\n", result)

// Custom error type
type ValidationError struct {
    Field   string
    Message string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("%s: %s", e.Field, e.Message)
}

func validateAge(age int) error {
    if age < 0 {
        return &ValidationError{
            Field:   "age",
            Message: "อายุต้องมากกว่าหรือเท่ากับ 0",
        }
    }
    if age > 120 {
        return &ValidationError{
            Field:   "age",
            Message: "อายุต้องไม่เกิน 120 ปี",
        }
    }
    return nil
}

// ใช้งาน custom error
err := validateAge(-5)
if err != nil {
    if verr, ok := err.(*ValidationError); ok {
        fmt.Printf("ตรวจสอบข้อมูลไม่ผ่าน: %s\n", verr)
    } else {
        fmt.Printf("เกิดข้อผิดพลาด: %v\n", err)
    }
}

Best Practices (แนวทางที่ดีที่สุด)

  1. ใช้ข้อความแสดงข้อผิดพลาดที่ชัดเจน - อธิบายว่าอะไรผิดพลาดและวิธีแก้ไข
  2. ไม่ควรใช้ try-catch ครอบโค้ดทั้งหมด - จัดการเฉพาะส่วนที่คาดว่าจะเกิดข้อผิดพลาด
  3. ใช้ประเภทข้อผิดพลาดที่เหมาะสม - ใช้ built-in error types หรือสร้าง custom error types
  4. บันทึกข้อผิดพลาด - บันทึกลง log file หรือระบบติดตามข้อผิดพลาด
  5. ตรวจสอบข้อผิดพลาดทุกครั้ง - อย่าลืมตรวจสอบค่า error ที่ได้จากการเรียกฟังก์ชัน
  6. ใช้ finally หรือ defer สำหรับการทำความสะอาดทรัพยากร
  7. เอกสารข้อผิดพลาดที่อาจเกิดขึ้น - บอกผู้ใช้ว่าอาจเกิดข้อผิดพลาดอะไรได้บ้าง

ตัวอย่างการจัดการข้อผิดพลาดแบบ Asynchronous

js
async function fetchUserData(userId) {
  try {
    const response = await fetch(`/api/users/${userId}`);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const data = await response.json();
    return data;
  } catch (error) {
    console.error("ไม่สามารถโหลดข้อมูลผู้ใช้ได้:", error);
    throw error; // ส่งต่อข้อผิดพลาดให้ caller จัดการต่อ
  }
}
rust
use reqwest;
use std::error::Error;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    match fetch_user_data(1).await {
        Ok(user) => println!("ข้อมูลผู้ใช้: {:?}", user),
        Err(e) => eprintln!("เกิดข้อผิดพลาด: {}", e),
    }
    Ok(())
}

async fn fetch_user_data(user_id: i32) -> Result<serde_json::Value, Box<dyn Error>> {
    let url = format!("https://api.example.com/users/{}", user_id);
    let response = reqwest::get(&url).await?;
    
    if !response.status().is_success() {
        return Err(Box::new(std::io::Error::new(
            std::io::ErrorKind::Other,
            format!("HTTP error! status: {}", response.status())
        )));
    }
    
    let user_data = response.json::<serde_json::Value>().await?;
    Ok(user_data)
}
python
import aiohttp
import asyncio

async def fetch_user_data(user_id):
    try:
        async with aiohttp.ClientSession() as session:
            async with session.get(f'https://api.example.com/users/{user_id}') as response:
                if response.status != 200:
                    raise Exception(f'HTTP error! status: {response.status}')
                return await response.json()
    except aiohttp.ClientError as e:
        print(f'เกิดข้อผิดพลาดในการเชื่อมต่อ: {e}')
        raise
    except Exception as e:
        print(f'เกิดข้อผิดพลาด: {e}')
        raise
go
package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "sync"
)

type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
    // เพิ่มฟิลด์อื่นๆ ตามต้องการ
}

func fetchUserData(userID int, wg *sync.WaitGroup, ch chan<- *User, errCh chan<- error) {
    defer wg.Done()
    
    url := fmt.Sprintf("https://api.example.com/users/%d", userID)
    resp, err := http.Get(url)
    if err != nil {
        errCh <- fmt.Errorf("ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์: %v", err)
        return
    }
    defer resp.Body.Close()
    
    if resp.StatusCode != http.StatusOK {
        errCh <- fmt.Errorf("HTTP error! status: %s", resp.Status)
        return
    }
    
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        errCh <- fmt.Errorf("ไม่สามารถอ่านข้อมูล: %v", err)
        return
    }
    
    var user User
    if err := json.Unmarshal(body, &user); err != nil {
        errCh <- fmt.Errorf("ไม่สามารถแปลงข้อมูล JSON: %v", err)
        return
    }
    
    ch <- &user
}

func main() {
    userIDs := []int{1, 2, 3}
    var wg sync.WaitGroup
    userCh := make(chan *User, len(userIDs))
    errCh := make(chan error, len(userIDs))
    
    // เรียก goroutines เพื่อดึงข้อมูลผู้ใช้แบบขนาน
    for _, id := range userIDs {
        wg.Add(1)
        go fetchUserData(id, &wg, userCh, errCh)
    }
    
    // รอให้ goroutines ทั้งหมดทำงานเสร็จ
    go func() {
        wg.Wait()
        close(userCh)
        close(errCh)
    }()
    
    // รับและแสดงผลลัพธ์
    for user := range userCh {
        fmt.Printf("ได้รับข้อมูลผู้ใช้: %+v\n", user)
    }
    
    // ตรวจสอบข้อผิดพลาด
    for err := range errCh {
        fmt.Printf("เกิดข้อผิดพลาด: %v\n", err)
    }
}

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