ป้ายกำกับ: โค้ดแอบแฝง

TypeScript: การใช้ Async/AwaitTypeScript: การใช้ Async/Await

ในโลกของการพัฒนาแอปพลิเคชันสมัยใหม่ ไม่ว่าจะเป็นระบบที่ต้องมีการเรียกใช้ API ภายนอก หรือการดึงข้อมูลขนาดใหญ่จากฐานข้อมูล การดำเนินการเหล่านี้ส่วนใหญ่จะไม่เกิดขึ้นแบบเรียงลำดับ (Synchronous) แต่จะเกิดขึ้นในลักษณะของการดำเนินการเบื้องหลัง (Asynchronous) ซึ่งเป็นกลไกสำคัญที่ทำให้แอปพลิเคชันของเราไม่เกิดอาการค้าง (Blocking) ขณะรอผลลัพธ์

เมื่อเราใช้ TypeScript ซึ่งเป็นภาษาที่เน้นความปลอดภัยของ Type System และทำงานบน Node.js การจัดการกับ Asynchronous Operations จึงเป็นหัวใจสำคัญที่นักพัฒนาต้องเข้าใจอย่างลึกซึ้ง

ปัญหาของการจัดการ Asynchronous แบบดั้งเดิม

ในอดีต การจัดการโค้ดที่ไม่พร้อมกันมักนำไปสู่ปัญหาที่เรียกว่า “Callback Hell” ซึ่งเกิดจากการซ้อนฟังก์ชัน Callback กันหลายชั้น ทำให้โครงสร้างโค้ดยุ่งเหยิง อ่านยาก และยากต่อการจัดการ Error ในยุคต่อมา Promises ได้ถูกนำมาใช้เพื่อปรับปรุงปัญหานี้ โดยการใช้ `.then()` chaining ทำให้การไหลของโค้ดดูเป็นระเบียบมากขึ้น

Async/Await: การยกระดับความอ่านง่ายด้วย Syntactic Sugar

TypeScript และ JavaScript ได้นำเสนอ Keyword async และ await เพื่อแก้ไขข้อจำกัดด้านความอ่านง่ายของ Promise Chain ทั้งหมดนี้ไม่ใช่คุณสมบัติใหม่ของภาษา แต่เป็นเพียงการเพิ่ม Syntactic Sugar ที่ช่วยให้เราสามารถเขียนโค้ด Asynchronous ให้มีโครงสร้างที่ดูเหมือนกับการทำงานแบบ Synchronous (เรียงลำดับ) ซึ่งช่วยให้โค้ดของเราสะอาดตาและเข้าใจง่ายขึ้นมาก

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

เมื่อเราต้องเขียนโค้ดที่ต้องเรียกใช้ PostgreSQL เพื่อดึงข้อมูลหลายขั้นตอน (เช่น ดึง User ID ก่อน แล้วจึงใช้ ID นั้นไปดึง Order Details) การใช้ async/await จะช่วยให้โค้ดของเราอ่านได้เหมือนการทำงานแบบขั้นตอนต่อขั้นตอน (Step-by-step) อย่างแท้จริง

สมมติว่าเราต้องการฟังก์ชัน TypeScript ที่รับ User ID แล้วทำการดึงข้อมูล Order ล่าสุดจากฐานข้อมูล PostgreSQL:

TypeScript

// ต้องมีการ import client จากไลบรารี PostgreSQL (เช่น pg) 
async function getLatestOrder(userId: number): Promise<string> {
    // 1. กำหนด Query และค่าพารามิเตอร์
    const queryText = "SELECT order_id, total_amount FROM orders WHERE user_id = $1 ORDER BY created_at DESC LIMIT 1;";
    const values = [userId];

    try {
        // 2. ใช้ 'await' เพื่อรอผลลัพธ์จากฐานข้อมูลอย่างเป็นระเบียบ
        // client.query() เป็นฟังก์ชัน Asynchronous
        const result = await client.query(queryText, values);

        if (result.rows.length > 0) {
            const order = result.rows[0];
            return `Order ID: ${order.order_id}, Total: ${order.total_amount}`;
        } else {
            return "ไม่พบคำสั่งซื้อสำหรับผู้ใช้งานนี้";
        }
    } catch (error) {
        // การจัดการ Error ทำได้ง่ายเหมือนโค้ด Synchronous
        console.error("Database Query Failed:", error);
        throw new Error("เกิดข้อผิดพลาดในการเชื่อมต่อฐานข้อมูล PostgreSQL");
    }
}

// การเรียกใช้ฟังก์ชันนี้ก็ต้องใช้ await เช่นกัน
(async () => {
    try {
        const result = await getLatestOrder(101);
        console.log(result);
    } catch (e) {
        console.error("Failure to process request.");
    }
})();

ข้อดีที่ได้รับจากการใช้ Async/Await

1. ความอ่านง่าย (Readability): โค้ดดูเป็นลำดับขั้นตอน ทำให้ง่ายต่อการทำความเข้าใจการไหลของข้อมูล (Data Flow) ของโปรแกรมเมอร์คนอื่น 2. การจัดการ Error (Error Handling): สามารถใช้บล็อก try...catch ได้โดยตรง เหมือนกับการจัดการ Error ในโค้ด Synchronous ทำให้การเขียนโค้ดมีความเสถียรสูง 3. ประสิทธิภาพ: ช่วยให้แอปพลิเคชันสามารถจัดการ I/O Bound Operations (เช่น การรอ DB) ได้อย่างมีประสิทธิภาพ โดยไม่ทำให้ Thread หลักของ Node.js ถูกบล็อก

บทสรุป

การทำความเข้าใจและใช้ async/await อย่างเชี่ยวชาญถือเป็นทักษะที่สำคัญอย่างยิ่งสำหรับ Senior Developer ในยุคที่แอปพลิเคชันต้องมีการสื่อสารกับบริการภายนอกและฐานข้อมูลอย่างต่อเนื่อง การใช้เครื่องมือเหล่านี้อย่างถูกต้อง ไม่เพียงแค่ทำให้โค้ดของเราดูดี แต่ยังช่วยให้เราสร้างระบบที่มีความเสถียรและดูแลรักษาได้ง่ายในระยะยาว