PlusMagi's Blog By Pitt Phunsanit Backend TypeScript Promises: คู่มือการจัดการ Asynchronous Code อย่างสมบูรณ์ด้วย Async/Await

TypeScript Promises: คู่มือการจัดการ Asynchronous Code อย่างสมบูรณ์ด้วย Async/Await

ในโลกของการพัฒนาซอฟต์แวร์สมัยใหม่ การจัดการโค้ดแบบ Asynchronous (ไม่พร้อมกัน) ถือเป็นหัวใจสำคัญอย่างยิ่งยวด ไม่ว่าจะเป็นการเรียก API, การดึงข้อมูลจากฐานข้อมูล (เช่น PostgreSQL), หรือการรอ Input จากผู้ใช้ ล้วนต้องเกิดขึ้นโดยที่โปรแกรมหลักไม่ต้องหยุดชะงักเพื่อรอนานๆ หากปราศจากการควบคุม Flow ของงานเหล่านี้ให้มีประสิทธิภาพ จะส่งผลให้เกิดปัญหาคลาสสิกที่เรียกว่า Callback Hell ซึ่งทำให้โครงสร้างของโค้ดยาว ยุ่งเหยิง และยากต่อการบำรุงรักษาในระดับสถาปัตยกรรม

TypeScript Promises: รากฐานสู่การจัดการ Asynchronous ที่สง่างาม

Promises คืออ็อบเจ็กท์ที่ถูกออกแบบมาเพื่อแทนค่าที่จะได้รับกลับคืนมาจาก Operation ที่จะเสร็จสมบูรณ์เมื่อไหร่ก็ได้ มันทำหน้าที่เหมือน ‘สัญญา’ ว่าจะมอบค่าบางสิ่ง ณ อนาคต โดยมีสถานะพื้นฐาน 3 รูปแบบ ได้แก่ Pending (กำลังดำเนินการ), Resolved (สำเร็จแล้วและได้ค่า), และ Rejected (ล้มเหลว) จุดเด่นที่สุดของ Promises คือการเปลี่ยนวงจร Callback แบบ Nested ให้กลายเป็นการ Chain อย่างต่อเนื่องผ่านเมธอด .then() สำหรับกรณีสำเร็จ และการรับมือข้อผิดพลาดด้วย .catch()

การก้าวข้าม Promise ด้วย Async/Await

แม้ว่า Promises จะช่วยให้โค้ดสะอาดขึ้นอย่างมาก แต่การใช้ .then().then().catch() หลายชั้นก็ยังดูซับซ้อนอยู่บ้าง Async/Await คือไวยากรณ์ (Syntactic Sugar) ที่ถูกสร้างขึ้นบนพื้นฐานของ Promises โดยมีเป้าหมายเพื่อทำให้โค้ด Asynchronous ดูเหมือนโค้ด Synchronous ทั่วไป ทำให้การอ่านและทำความเข้าใจ Flow ของการทำงานเป็นไปอย่างเป็นธรรมชาติและง่ายต่อการ Debugging ในระดับ Senior Architect

การประยุกต์ใช้ในบริบทของ PostgreSQL Backend

ในระบบที่ใช้ PostgreSQL เป็น Backend การเรียกข้อมูลมักเป็นกระบวนการที่ต้องทำตามลำดับขั้นตอน (Sequential Dependency) เช่น: 1) ตรวจสอบผู้ใช้ 2) ดึงรายการสินค้าที่เกี่ยวข้อง 3) อัปเดตสถานะคำสั่งซื้อ การใช้ async/await ช่วยให้เราสามารถจัดการ Dependency เหล่านี้ได้อย่างชัดเจน โดยโค้ดจะ ‘รอ’ (await) ให้ขั้นตอนก่อนหน้าเสร็จสมบูรณ์ก่อนจึงจะเริ่มขั้นตอนถัดไป ซึ่งเป็นการจำลองการทำงานของ Transaction ที่ต้องสำเร็จตามลำดับในฐานข้อมูล

นี่คือตัวอย่างการจำลองการเรียกใช้ PostgreSQL ผ่าน TypeScript/Node.js ที่ต้องรอผลลัพธ์เป็นลำดับ:

TypeScript

// สมมติว่าเรามีฟังก์ชันที่เรียกใช้ PostgreSQL และคืนค่าเป็น Promise<T>\nasync function processOrder(orderId: number, userId: number): Promise<any> {\n  try {\n    // Step 1: await การดึงข้อมูลคำสั่งซื้อ (ต้องรอให้ Query เสร็จก่อน)\n    const orderResult = await db.query('SELECT * FROM orders WHERE id = $1', [orderId]);\n    const order = orderResult.rows[0];\n
    // Step 2: await การตรวจสอบสิทธิ์ผู้ใช้ (ต้องรอผลลัพธ์จาก Step 1 ก่อน)\n    if (order.user_id !== userId) {\n      throw new Error('Unauthorized access.');\n    }\n
    // Step 3: await การอัปเดตสถานะ (ต้องรอให้การตรวจสอบสำเร็จก่อน)\n    const updateResult = await db.query('UPDATE orders SET status = $1 WHERE id = $2', ['Processed', orderId]);\n    return { success: true, message: 'Order processed successfully.' };\n
  } catch (error) {\n    // การจัดการ Error ที่ถูกจับได้ในทุกขั้นตอน\n    console.error('Transaction failed:', error.message);\n    throw error;\n  }\n}\n

บทสรุป: Best Practice ในการพัฒนา

ในฐานะ System Architect การใช้ async/await ไม่ได้เป็นเพียงแค่ทางเลือก แต่เป็น Best Practice ที่จำเป็นในการเขียนโค้ดที่จัดการ I/O Bound Operations อย่างฐานข้อมูลได้อย่างมีประสิทธิภาพ มันช่วยให้โค้ดของเรามีความอ่านง่าย (Readability) สูงมาก ลดโอกาสเกิด Bug จากการจัดการ State ที่ซับซ้อน และทำให้เราสามารถสร้างระบบ Backend ที่มีความน่าเชื่อถือและ Scalable ได้อย่างแท้จริง