Try-Catch: สาระสำคัญ โครงสร้างการดักจับข้อยกเว้นและการทำงานในการพัฒนาแอปมือถือ

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-05-25 เวลาอ่าน: 9 นาที

Try-Catch คือโครงสร้างการดักจับข้อยกเว้นที่ช่วยให้สามารถเรียกใช้โค้ดที่อาจเป็นอันตรายในบล็อกที่ป้องกันและจัดการข้อผิดพลาดได้อย่างถูกต้องโดยไม่ทำให้โปรแกรมหยุดทำงานกะทันหัน บล็อก try มีโค้ดที่อาจโยนข้อยกเว้น catch จะดักจับมันและดำเนินการลอจิกการกู้คืน ตาม Apple Swift Documentation (2026) บล็อก finally จะทำงานไม่ว่าจะมีการโยนข้อยกเว้นหรือไม่ก็ตาม เพื่อรับประกันการปลดปล่อยทรัพยากร

ประเด็นสำคัญ

  • Try-Catch คือโครงสร้างการดักจับข้อยกเว้นที่ป้องกันการหยุดทำงานของโปรแกรมเมื่อเกิดข้อผิดพลาด
  • บล็อก try มีโค้ดที่อาจโยนข้อยกเว้น — การทำงานจะหยุดที่ข้อผิดพลาดแรก
  • บล็อก catch ดักจับข้อยกเว้นของประเภทที่ระบุและดำเนินการลอจิกการจัดการหรือกู้คืน
  • บล็อก finally จะทำงานหลังจาก try/catch อย่างแน่นอนเพื่อปลดปล่อยทรัพยากร เช่น ปิดไฟล์
  • Try-catch ที่ซ้อนกัน ช่วยให้จัดการข้อผิดพลาดในระดับนามธรรมที่แตกต่างกันภายในฟังก์ชันเดียวกัน

Try-Catch คืออะไร?

Try-Catch คือโครงสร้างการจัดการข้อยกเว้นเชิงโครงสร้างพื้นฐานที่มีอยู่ในภาษาโปรแกรมมิ่งสมัยใหม่ส่วนใหญ่ ประกอบด้วยสามบล็อก: try (พยายามเรียกใช้โค้ดอันตราย), catch (ดักจับและจัดการข้อยกเว้น) และ finally (การสิ้นสุด) ซึ่งเป็นทางเลือก แนวคิดของโครงสร้างนี้คือการแยกลอจิกทางธุรกิจออกจากลอจิกการจัดการข้อผิดพลาด ทำให้โค้ดอ่านง่ายและคาดการณ์ได้มากขึ้น

แนวคิดนี้ถูกนำมาใช้ครั้งแรกในภาษา C++ ในรูปแบบ try/catch จากนั้นถูกนำไปใช้โดย Java, C#, Swift, Kotlin, Dart, Python, JavaScript และภาษาอื่นๆ แต่ละภาษาเพิ่มคุณลักษณะเฉพาะของตนเอง: ใน Swift บล็อก catch จะต้องครอบคลุมทั้งหมด ใน Kotlin try-catch สามารถเป็นนิพจน์ได้ ใน Dart finally เป็นข้อบังคับสำหรับทรัพยากรสตรีม แม้จะมีความแตกต่างกัน หลักการพื้นฐานก็เหมือนกัน: ข้อผิดพลาดจะถูกจัดการให้ใกล้กับจุดที่เกิดขึ้นมากที่สุด ไม่ใช่ในระดับโลก

การใช้ Try-Catch มีความสำคัญเป็นพิเศษในการพัฒนาแอปมือถือ ซึ่งปัจจัยภายนอก — การสูญเสียเครือข่าย การตอบสนองของเซิร์ฟเวอร์ที่ไม่ถูกต้อง หน่วยความจำไม่เพียงพอ — เกิดขึ้นอย่างต่อเนื่อง การจัดการข้อยกเว้นที่เหมาะสมช่วยป้องกันการหยุดทำงานของแอปพลิเคชันและรับประกัน UX ที่ถูกต้อง: ผู้ใช้เห็นข้อความแสดงข้อผิดพลาดแทนการปิดแอปพลิเคชันอย่างกะทันหัน ตาม Google Android Kotlin Style Guide (2026) ทุกฟังก์ชันที่อาจโยนข้อยกเว้นควรจัดการผ่าน try-catch หรือประกาศ throws ในลายเซ็นของมัน

Try-Catch ทำงานอย่างไร

กลไกการทำงานของ try-catch ขึ้นอยู่กับการคลายสแต็ก (stack unwinding) เมื่อมีการโยนข้อยกเว้นภายในบล็อก try ผ่านโอเปอเรเตอร์ throw (หรือเป็นผลจากข้อผิดพลาดของระบบ) การไหลของการทำงานปกติจะถูกขัดจังหวะทันที การทำงานจะเลื่อนขึ้นไปหนึ่งระดับในสแต็กการเรียกเพื่อค้นหาบล็อก catch ที่เหมาะสม ภาษาสมัยใหม่ค้นหา catch ที่มีประเภทตรงกับประเภทของข้อยกเว้นที่ถูกโยนโดยใช้ กลไกการจับคู่ประเภท (type matching)

หากพบ catch ที่ตรงกัน เนื้อหาของมันจะถูกดำเนินการ หลังจากนั้นการทำงานจะดำเนินต่อหลังจากโครงสร้าง try-catch-finally ทั้งหมด หากไม่พบ catch ใดๆ ข้อยกเว้นจะเลื่อนขึ้นไปในสแต็กและอาจถูกจัดการในระดับที่สูงขึ้น — ไปจนถึงตัวจัดการระดับโลก ซึ่งในแอปพลิเคชันมือถือจะแสดงไดอะล็อกข้อผิดพลาดให้ผู้ใช้เห็น หากข้อยกเว้นไม่ถูกจัดการที่ใดเลย แอปพลิเคชันจะหยุดทำงาน นี่คือเหตุผลที่ การจัดการที่ถูกต้องของข้อยกเว้นทุกประเภทที่เป็นไปได้มีความสำคัญต่อความเสถียรของแอปพลิเคชัน

ไวยากรณ์ของโครงสร้างพื้นฐาน

kotlin
fun readUserData(): User {
    return try {
        val response = api.fetchUser()
        parseUser(response)
    } catch (e: IOException) {
        logError("ข้อผิดพลาดเครือข่าย", e)
        throw AppException("ไม่สามารถโหลดข้อมูลได้")
    } catch (e: JsonParseException) {
        logError("ข้อผิดพลาดการแยกวิเคราะห์", e)
        return User.default()
    } finally {
        closeLoadingIndicator()
    }
}

โค้ดจะพยายามทำคำขอไปยัง API และแยกวิเคราะห์การตอบสนองก่อน หากเกิด IOException (ปัญหาเครือข่าย) ข้อยกเว้นจะถูกบันทึกและโยนซ้ำเป็น AppException หากเกิด JsonParseException — ผู้ใช้เริ่มต้นจะถูกส่งคืน บล็อก finally รับประกันการซ่อนตัวบ่งชี้การโหลด ป้องกันการรั่วไหลของส่วนประกอบ UI บนหน้าจอ

Try-Catch ใน Swift

ใน Swift การจัดการข้อผิดพลาดถูกนำมาใช้ผ่านโปรโตคอล Error (เดิมคือ ErrorType) ประเภทใดก็ตามที่สอดคล้องกับ Error สามารถโยนผ่านโอเปอเรเตอร์ throw ได้ ฟังก์ชันที่สามารถโยนข้อผิดพลาดจะถูกทำเครื่องหมายด้วยคีย์เวิร์ด throws ในลายเซ็นของมัน การเรียกฟังก์ชันดังกล่าวต้องใช้นำหน้า try (สำหรับ try-catch ที่ชัดเจน), try? (ผลลัพธ์ทางเลือก) หรือ try! (การบังคับทำงานโดยไม่จัดการข้อผิดพลาด)

swift
enum NetworkError: Error {
    case noConnection
    case serverError(code: Int)
    case timeout
}

func fetchUser(id: Int) throws -> User {
    guard isConnected() else {
        throw NetworkError.noConnection
    }
    let data = try performRequest(path: "/users/\(id)")
    return try decodeUser(from: data)
}

do {
    let user = try fetchUser(id: 42)
    updateUI(user)
} catch NetworkError.noConnection {
    showOfflineAlert()
} catch let error as NetworkError {
    showError("เครือข่าย " + error.localizedDescription)
} catch {
    showGenericError()
}

Enum NetworkError ใช้โปรโตคอล Error โดยกำหนดสามกรณี: noConnection, serverError พร้อมรหัส และ timeout ฟังก์ชัน fetchUser ถูกทำเครื่องหมายด้วย throws: ตรวจสอบการเชื่อมต่อก่อน จากนั้นดำเนินการตามคำขอและแยกวิเคราะห์ ในบล็อก do-catch สาม catch จัดการสถานการณ์ที่แตกต่างกัน: กรณีเฉพาะ noConnection, ประเภททั่วไป NetworkError และข้อผิดพลาดอื่นๆ ทั้งหมด ซึ่งช่วยให้แสดงข้อความที่แตกต่างกันให้ผู้ใช้เห็นตามประเภทของปัญหา

Try-Catch ใน Kotlin

Kotlin สืบทอด try-catch-finally จาก Java แต่เพิ่มความแตกต่างที่สำคัญ: ใน Kotlin try-catch เป็น นิพจน์ (expression) ไม่ใช่คำสั่ง (statement) ซึ่งหมายความว่าผลลัพธ์ของบล็อก try หรือบล็อก catch สามารถกำหนดให้กับตัวแปรได้ นิพจน์สุดท้ายในบล็อก try จะกลายเป็นผลลัพธ์เมื่อสำเร็จ นิพจน์สุดท้ายใน catch — เมื่อเกิดข้อผิดพลาด หากข้อผิดพลาดไม่ถูกจัดการโดย catch ใดๆ ข้อยกเว้นจะเลื่อนขึ้นไปในสแต็ก

kotlin
sealed class Result<out T> {
    data class Success<out T>(val data: T) : Result<T>()
    data class Error(val exception: Throwable) : Result<Nothing>()
}

fun loadData(): Result<List<Item>> {
    return try {
        val response = api.getItems()
        Result.Success(response.toList())
    } catch (e: HttpException) {
        Log.e("HTTP ", e)
        Result.Error(e)
    } catch (e: IOException) {
        Log.e("เครือข่าย ", e)
        Result.Error(e)
    }
}

ในตัวอย่าง sealed class Result ห่อหุ้มการตอบสนองที่สำเร็จหรือข้อผิดพลาด ฟังก์ชัน loadData ใช้ try-catch เป็นนิพจน์: เมื่อสำเร็จจะส่งคืน Result.Success เมื่อ HttpException หรือ IOException — Result.Error พร้อมการบันทึก วิธีการนี้ช่วยให้ผู้เรียกจัดการข้อผิดพลาดโดยไม่ต้องใช้ข้อยกเว้น — ผ่านนิพจน์ when บนประเภท Result ซึ่งสะดวกเป็นพิเศษใน Jetpack Compose สำหรับการแสดงสถานะ UI ที่แตกต่างกัน (Loading, Success, Error) ผ่าน StateFlow และ collectAsState

Try-Catch ใน Dart และ Flutter

Dart รองรับ try-catch-finally ด้วยไวยากรณ์คล้ายกับ Java แต่เพิ่ม on clause สำหรับกรองตามประเภทข้อยกเว้นโดยไม่ต้องระบุตัวแปร ซึ่งสะดวกเมื่อไม่จำเป็นต้องใช้ข้อยกเว้นเอง — เพียงแค่ประเภทของมันก็เพียงพอแล้ว Dart ยังรองรับบล็อก catch ด้วยสองพารามิเตอร์: ออบเจกต์ข้อยกเว้นและ StackTrace ซึ่งมีประโยชน์สำหรับการบันทึกห่วงโซ่การเรียกทั้งหมด

dart
import 'dart:io';
import 'dart:convert';

class UserRepository {
    Future<User> fetchUser(String id) async {
        try {
            final client = HttpClient();
            final request = await client.getUrl(
                Uri.parse('https://api.example.com/users/$id')
            );
            final response = await request.close();
            final body = await response.transform(utf8.decoder).join();
            return User.fromJson(json.decode(body));
        } on SocketException catch (e, stackTrace) {
            log("No internet", e, stackTrace);
            throw AppException("Connection failed");
        } on FormatException {
            throw AppException("Invalid response format");
        } finally {
            client.close();
        }
    }
}

SocketException ถูกดักจับพร้อมกับออบเจกต์ข้อยกเว้นและ StackTrace สำหรับการบันทึกโดยละเอียด จากนั้นโยนซ้ำเป็น AppException FormatException ถูกดักจับโดยไม่มีตัวแปร — แค่รู้ว่ารูปแบบการตอบสนองไม่ถูกต้องก็เพียงพอแล้ว บล็อก finally รับประกันการปิด HttpClient ป้องกันการรั่วไหลของซ็อกเก็ต ใน Flutter วิธีการนี้สำคัญเป็นพิเศษสำหรับการทดสอบ Widget ซึ่งข้อยกเว้นที่ไม่ถูกจัดการใน State.initState ทำให้เซสชันการทดสอบทั้งหมดล้มเหลว

ข้อผิดพลาดทั่วไปเมื่อใช้ Try-Catch

แม้แต่นักพัฒนาที่มีประสบการณ์ก็ยังทำผิดพลาดเมื่อทำงานกับ try-catch ซึ่งนำไปสู่การรั่วไหลของหน่วยความจำ บั๊กที่ซ่อนอยู่ หรือพฤติกรรมที่ไม่เหมาะสมของแอปพลิเคชัน มาดูปัญหาที่พบบ่อยที่สุดห้าประการในการพัฒนาแอปมือถือ

บล็อก catch ว่างเปล่า

catch ว่างเปล่า เป็นหนึ่งในแนวปฏิบัติที่แย่ที่สุด ข้อยกเว้นถูกกลืนกิน แอปพลิเคชันยังคงทำงานในสถานะที่ไม่ถูกต้อง และนักพัฒนาไม่รู้เกี่ยวกับปัญหา อย่างน้อยควรบันทึกข้อยกเว้นเสมอ ใน Kotlin ใช้ catch(e: Exception) { Log.e(...) }, ใน Swift — catch { print($0) } ใน Dart catch ที่ยอมรับได้ขั้นต่ำควรเรียก debugPrint หรือเขียนไปยัง Crashlytics

catch ที่กว้างเกินไป

การดักจับข้อยกเว้นทั้งหมดผ่าน catch (Exception e) โดยไม่แยกตามประเภทจะซ่อนข้อผิดพลาดที่ไม่คาดคิด — NullPointerException, OutOfMemoryError, StackOverflowError ดักจับเฉพาะประเภทที่คุณคาดหวังและสามารถจัดการได้ สำหรับสิ่งอื่นๆ ปล่อยให้แพร่กระจายขึ้นไป ในการพัฒนาแอปมือถือ catch เฉพาะสำหรับ IOException, TimeoutException, AuthException ให้ข้อความที่มีความหมายมากขึ้นแก่ผู้ใช้

ละเลย finally

ทรัพยากร — ไฟล์, ซ็อกเก็ต, เคอร์เซอร์ฐานข้อมูล, แอนิเมชัน — ควรถูกปลดปล่อยใน finally หรือใน use-block (AutoCloseable) นักพัฒนามักลืมปิดทรัพยากรเมื่อเกิดข้อยกเว้น ซึ่งนำไปสู่การรั่วไหล ใน Kotlin ใช้ .use { } สำหรับทรัพยากร Closeable, ใน Swift — defer { }, ใน Dart — await using จากแพ็คเกจ async บล็อก finally รับประกันการปลดปล่อยแม้เมื่อมีการโยนข้อยกเว้นภายใน catch

ข้อยกเว้นในเธรดและคอรูน

ในโค้ดอะซิงโครนัส try-catch ไม่สามารถดักจับข้อยกเว้นจากเธรดอื่นได้ ใน Kotlin Coroutines ใช้ CoroutineExceptionHandler หรือ SupervisorJob ใน Swift async/await — do-catch ภายใน Task ใน Flutter — runZonedGuarded สำหรับการดักจับระดับโลก การละเลยกฎนี้เป็นสาเหตุของความล้มเหลวที่ยากจะทำซ้ำในโปรดักชัน

การบล็อก UI เมื่อเกิดข้อยกเว้น

การจัดการข้อยกเว้นไม่ควรบล็อกอินเตอร์เฟซผู้ใช้อย่างไม่มีกำหนด แสดงข้อความเฉพาะให้ผู้ใช้เห็นและให้โอกาสพวกเขาลองดำเนินการอีกครั้ง Snackbar พร้อมปุ่ม Retry ใน Kotlin/Compose, UIAlertController พร้อม action ใน Swift, SnackBar พร้อม action ใน Flutter — UX ที่เพียงพอขั้นต่ำสำหรับข้อผิดพลาดเครือข่ายหรือเซิร์ฟเวอร์ หลีกเลี่ยงไดอะล็อกทั่วไป “เกิดข้อผิดพลาด” โดยไม่มีตัวเลือกการกู้คืน

คำถามที่พบบ่อย

try-catch แตกต่างจาก Result Type อย่างไร?

Try-catch ใช้ข้อยกเว้นและการคลายสแต็กสำหรับการจัดการข้อผิดพลาด ซึ่งอาจมีต้นทุนด้านประสิทธิภาพสูงเมื่อมีข้อผิดพลาดจำนวนมาก Result Type เป็นประเภทคอนเทนเนอร์ (Success หรือ Failure) ที่จัดการผ่านการจับคู่รูปแบบ (pattern matching) โดยไม่ต้องคลายสแต็ก ซึ่งมีประสิทธิภาพมากกว่าสำหรับข้อผิดพลาดที่คาดการณ์ได้

จำเป็นต้องใช้ finally ในทุก try-catch หรือไม่?

Finally จำเป็นถ้าบล็อก try เปิดทรัพยากร (ไฟล์, ซ็อกเก็ต, เคอร์เซอร์) ที่ต้องปิด หากไม่มีการเปิดทรัพยากร finally ไม่จำเป็น ในภาษาสมัยใหม่ ใช้ AutoCloseable/use/defer สำหรับการปิดทรัพยากรอัตโนมัติโดยไม่ต้องใช้ finally use-block ใน Kotlin และ Swift แทนที่ finally สำหรับออบเจกต์ Closeable

try-catch ช้าได้หรือไม่?

ในการไหลปกติ (ไม่มีข้อยกเว้น) try-catch แทบไม่มีผลกระทบต่อประสิทธิภาพ — JVM และคอมไพเลอร์ Swift ปรับกรณีนี้ให้เหมาะสม แต่เมื่อมีการโยนข้อยกเว้น การคลายสแต็กจะเกิดขึ้น ซึ่งอาจใช้เวลา 10–100 μs ขึ้นอยู่กับความลึกของสแต็ก อย่าใช้ข้อยกเว้นสำหรับการควบคุมการไหล — นี่เป็นรูปแบบที่ไม่ดี

จะจัดการข้อผิดพลาดใน Kotlin Coroutines ได้อย่างไร?

ในคอรูน ใช้ try-catch ภายใน coroutineScope หรือ CoroutineExceptionHandler สำหรับการดักจับระดับโลก SupervisorJob ป้องกันการยกเลิกคอรูนแม่เมื่อคอรูนลูกล้มเหลว สำหรับ launch ใช้ CoroutineExceptionHandler สำหรับ async — try-catch รอบ await()

อันไหนดีกว่า: หลาย catch หรือ catch เดียวกับ if-else?

หลาย catch ดีกว่า: โค้ดอ่านเป็นเส้นตรง แต่ละบล็อกจัดการข้อยกเว้นหนึ่งประเภท catch เดียวกับ if-else ดูแลรักษายากกว่าและพลาดข้อยกเว้นประเภทใหม่ได้ง่าย ใน Swift หลาย catch เป็นข้อบังคับสำหรับการจัดการ enum Error ที่ครอบคลุม ใน Kotlin ไม่มีข้อจำกัด แต่แนวปฏิบัติที่ดีที่สุดคือ catch แยกตามประเภท

สรุป

  • Try-Catch คือโครงสร้างการดักจับข้อยกเว้นด้วยบล็อก try, catch และ finally ที่เป็นทางเลือกสำหรับการปลดปล่อยทรัพยากรที่รับประกัน
  • กลไกการทำงาน — การคลายสแต็กเมื่อโยนข้อยกเว้นและค้นหา catch ที่เหมาะสมตามประเภทข้อผิดพลาด
  • ใน Swift ใช้ do-catch กับ enum Error, try? สำหรับผลลัพธ์ทางเลือก และ try! สำหรับความสำเร็จที่รับประกัน
  • ใน Kotlin try-catch เป็นนิพจน์ที่สามารถกำหนดผลลัพธ์ให้ตัวแปรได้ สะดวกกับ sealed class Result
  • ใน Dart รองรับ on clause สำหรับกรองตามประเภทโดยไม่ต้องใช้ตัวแปรและ finally สำหรับปิด HttpClient
  • ข้อผิดพลาดทั่วไป: catch ว่างเปล่า, catch กว้างเกินไป, ละเลย finally และขาดการจัดการในคอรูน
  • ใช้ try-catch สำหรับ ข้อผิดพลาดที่ไม่คาดคิด, Result Type — สำหรับสถานการณ์ที่คาดการณ์ได้ซึ่งอาจล้มเหลว

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม