PlusMagi's Blog By Pitt Phunsanit Backend TypeScript Async/Await: คู่มือการเขียนโค้ด Asynchronous ฉบับสมบูรณ์

TypeScript Async/Await: คู่มือการเขียนโค้ด Asynchronous ฉบับสมบูรณ์

ในโลกของการพัฒนาซอฟต์แวร์สมัยใหม่ โดยเฉพาะอย่างยิ่งในการพัฒนา Backend ที่ต้องมีการติดต่อกับฐานข้อมูล (เช่น PostgreSQL) การทำงานแบบอะซิงโครนัส (Asynchronous Programming) เป็นหัวใจสำคัญที่ช่วยให้โปรแกรมไม่หยุดชะงักเมื่อต้องรอการดำเนินการภายนอก เช่น การเรียก API ข้อมูล หรือการรัน Query ข้อมูลขนาดใหญ่ หากปราศจากการจัดการ Asynchronicity ที่เหมาะสม โค้ดของเราจะกลายเป็นรูปแบบเชิงเส้นตรงที่ไม่สามารถรองรับงานหลายอย่างพร้อมกันได้ บทความนี้คือคู่มือฉบับสมบูรณ์ที่จะพาคุณเจาะลึกถึงพลังของ TypeScript Async/Await ซึ่งเป็นเครื่องมืออันทรงประสิทธิภาพในการเขียนโค้ดสไตล์ Promise ให้ดูเหมือนกับการไหลเวียนของคำสั่งปกติ

ทำไมจึงจำเป็นต้องใช้ Async/Await ในงาน Backend?

เดิมที เมื่อเราพูดถึง JavaScript และ Promises เรามักจะต้องใช้วิธี `.then().catch()` ต่อเนื่องไปเรื่อยๆ แม้ว่าวิธีนี้จะเป็นมาตรฐานที่ดีกว่า Callback Hell อย่างมาก แต่ก็ยังทำให้โครงสร้างโค้ดยาวและขาดความเป็นธรรมชาติทางสายตา ทำให้ยากต่อการติดตาม Flow ของ Logic โดยเฉพาะเมื่อมีการข้ามขั้นตอนหรือต้องการจัดกลุ่ม Error Handling

Async/Await จึงเข้ามาแก้ไขปัญหานี้โดยการยกระดับภาษาให้สามารถจัดการกับค่าสัญญา (Promise Resolution Value) ได้อย่างอ่านง่ายเสมือนตัวแปรที่ถูกกำหนดค่าในลำดับเวลาจริง ทำให้โค้ดที่ต้องรอผลลัพธ์จากฐานข้อมูล (เช่น การรัน Query ของ PostgreSQL) มีความสะอาดและเป็นระเบียบมากขึ้นอย่างเห็นได้ชัด

หลักไวยากรณ์: การทำงานร่วมกับ Keyword หลัก

  • Async: ต้องประกาศไว้หน้าฟังก์ชัน (async function) เพื่อบอกให้ TypeScript/JavaScript Compiler ทราบว่า ฟังก์ชันนี้จะนำเสนอผลลัพธ์ที่เป็น Asynchronous เสมอ ดังนั้นมันจะส่งคืนค่าเป็น Promise เสมอ ไม่ว่าคุณจะคืนค่าอะไรก็ตาม
  • Await: เป็น Keyword ที่สำคัญที่สุด มันจะทำหน้าที่ ‘หยุด’ การทำงานของฟังก์ชัน async ชั่วคราว จนกว่า Promise ที่อยู่ด้านหน้า await จะถูก Resolve (สำเร็จ) หรือ Reject (ล้มเหลว) เมื่อ Promise นั้นเสร็จสมบูรณ์ ค่าที่ได้จะถูกส่งคืนมาให้เราใช้งานในรูปแบบของค่าปกติ

ตัวอย่างการใช้งานกับ PostgreSQL Query

สมมติว่าเราต้องการดึงข้อมูลผู้ใช้จาก PostgreSQL และหลังจากนั้นจึงอัปเดตสถานะการเข้าสู่ระบบ เราสามารถใช้ Async/Await เพื่อให้โค้ดดูเหมือนการทำงานแบบเชิงเส้นตรง (Sequential) ได้อย่างสมบูรณ์

SQL

// สมมติว่า 'pool' คือ PostgreSQL Connection Pool ที่ถูกตั้งค่าไว้แล้ว

async function fetchAndLogUser(userId: number): Promise<string> {
    try {
        // 1. ใช้ await เพื่อรอผลลัพธ์จากการ Query ข้อมูลผู้ใช้
        const result = await pool.query('SELECT username, email FROM users WHERE id = $1', [userId]);
        
        // 2. ตรวจสอบผลลัพธ์และดึงข้อมูลออกมา
        if (result.rows.length === 0) {
            throw new Error("User not found.");
        }
        
        const userData = result.rows[0];
        console.log(`[SUCCESS] Found user: ${userData.username}`);

        // 3. ทำการ Query ครั้งที่สอง (เช่น การอัปเดตสถานะ) และรอผลลัพธ์อีกครั้ง
        await pool.query('UPDATE user_logs SET last_login = NOW() WHERE user_id = $1', [userId]);
        
        return `Successfully processed user ${userData.username}.`;

    } catch (error) {
        // การจัดการ Error ที่ง่ายและเป็นระเบียบด้วย try...catch
        console.error("Database operation failed:", error);
        throw new Error("Failed to process user data due to database error.");
    }
}

// การเรียกใช้ฟังก์ชัน async
fetchAndLogUser(1);

บทสรุป: พลังแห่งความอ่านง่ายและประสิทธิภาพ

การใช้ TypeScript Async/Await ไม่ได้เป็นเพียงแค่ทางเลือก แต่เป็นมาตรฐานในการเขียนโค้ด Asynchronous ในยุคปัจจุบัน มันช่วยให้เราหลีกหนีจากความซับซ้อนของ `.then()` chain และทำให้โค้ดของเรามีโครงสร้างที่อ่านง่าย (Readability) เหมือนกับการเขียนโค้ดแบบ Synchronous ทั่วไป

ในฐานะ Senior Architect การเข้าใจและใช้ Async/Await อย่างเชี่ยวชาญ โดยเฉพาะเมื่อต้องจัดการกับ I/O Operations ที่เกี่ยวข้องกับ PostgreSQL จะช่วยให้คุณสามารถสร้างระบบ Backend ที่มีประสิทธิภาพสูง จัดการ Error ได้อย่างเป็นระเบียบ และที่สำคัญที่สุดคือทำให้โค้ดของคุณเป็นมิตรต่อการบำรุงรักษา (Maintainable) อย่างแท้จริง