ในโลกของการพัฒนาแอปพลิเคชันระดับองค์กร (Enterprise Application) การโต้ตอบกับแหล่งข้อมูลภายนอก เช่น การเรียกใช้ API หรือการดำเนินการกับฐานข้อมูล PostgreSQL มักเป็นกระบวนการที่ใช้เวลานานกว่าการประมวลผลภายในเครื่อง (I/O Bound Operation) ซึ่งเราเรียกรวมกันว่า Asynchronous Operation
หากไม่มีกลไกจัดการที่เหมาะสม โค้ดของเราอาจประสบปัญหา Callback Hell ทำให้โครงสร้างโค้ดซับซ้อน ยากต่อการอ่าน และการจัดการข้อผิดพลาด (Error Handling) ก็ทำได้ลำบาก ด้วยเหตุนี้ TypeScript Promises จึงถูกพัฒนาขึ้นมาเป็นมาตรฐานในการจัดการการทำงานแบบอะซิงครอน ทำให้โค้ดของเรามีความเป็นระเบียบ (Sequential) และสามารถจัดการการไหลของข้อมูล (Data Flow) ได้อย่างแม่นยำ
ทำไม Promise จึงจำเป็นในการเชื่อมต่อกับ PostgreSQL
หัวใจสำคัญของการทำงานกับฐานข้อมูลคือการที่ระบบต้องรอผลลัพธ์จากการ Query ซึ่งเป็นกระบวนการที่ใช้เวลา Promises เข้ามาแก้ไขปัญหานี้โดยการห่อหุ้ม (Wrap) การดำเนินการที่ใช้เวลานานให้กลายเป็นวัตถุที่สามารถระบุสถานะได้ (Pending, Fulfilled, Rejected) ทำให้เราสามารถใช้ไวยากรณ์ .then() และ .catch() เพื่อจัดการลำดับขั้นตอนได้อย่างเป็นระเบียบ
การใช้ Async/Await เพื่อความสะอาดของโค้ด
ในระดับ Senior Architect เราไม่เพียงแค่ใช้ Promise แต่เราจะใช้ไวยากรณ์ async/await ซึ่งเป็น Syntax Sugar ที่สร้างขึ้นบน Promise โดยตรง ทำให้โค้ดที่เคยต้องใช้ .then().then().catch() กลายเป็นโค้ดที่อ่านได้เหมือนการทำงานแบบ Synchronous (Sequential) แม้ว่าเบื้องหลังจะยังคงเป็น Non-blocking I/O ก็ตาม
ตัวอย่างการจัดการ Transaction Asynchronous กับ PostgreSQL
สมมติว่าเราต้องการดำเนินการหลายขั้นตอน เช่น การตรวจสอบข้อมูล การดึงข้อมูล และการอัปเดตข้อมูลใน PostgreSQL โดยต้องให้ทุกขั้นตอนสำเร็จตามลำดับ หากขั้นตอนใดล้มเหลว จะต้องยกเลิกการทำงานทั้งหมด (Rollback) นี่คือตัวอย่างการจำลองการทำงานใน TypeScript/Node.js ที่ใช้ Promise เพื่อควบคุม Flow:
SQL
-- สมมติว่าฟังก์ชันนี้ถูกห่อด้วย Promise และเชื่อมต่อกับ PostgreSQL Client</code>
function processDataAsync(connection: Connection, dataIds: string[]) {
return connection.query("SELECT * FROM products WHERE id = $1", [dataIds])
.then(result => {
// ขั้นตอนที่ 1: ดึงข้อมูล (Promise 1)
console.log("Step 1: Data retrieved successfully.");
return connection.query("UPDATE products SET status = 'processed' WHERE id = $1", [dataIds]);
})
.then(updateResult => {
// ขั้นตอนที่ 2: อัปเดตข้อมูล (Promise 2)
console.log("Step 2: Data updated successfully.");
return connection.query("INSERT INTO logs (data) VALUES ($1)", ['1,2,3']);
})
.then(() => {
// ขั้นตอนที่ 3: บันทึก Log (Promise 3)
console.log("Step 3: Transaction completed successfully.");
return "Transaction Success";
})
.catch(error => {
// การจัดการข้อผิดพลาด (Error Handling)
console.error("Transaction Failed: ", error);
throw new Error("Database transaction failed.");
});
}
ในโค้ดนี้ เราใช้ .then() เพื่อรับผลลัพธ์ของ Query ก่อนหน้า และส่งต่อการทำงานไปยัง Query ถัดไป ทำให้มั่นใจได้ว่าการดำเนินการทั้งหมดจะเกิดขึ้นตามลำดับที่กำหนด และหากมี Query ใดล้มเหลว (เช่น การเชื่อมต่อหลุด หรือ Constraint Violation) ระบบจะถูกส่งไปยัง .catch() ทันที ทำให้การจัดการ Transaction มีความน่าเชื่อถือสูง
บทสรุป
การใช้ TypeScript Promises ร่วมกับแนวคิด async/await ไม่ใช่แค่การปรับปรุงไวยากรณ์ แต่คือการยกระดับสถาปัตยกรรมโค้ดให้มีความเป็นระเบียบ (Readability) และความสามารถในการจัดการข้อผิดพลาด (Robustness) ที่สูงขึ้นอย่างมาก สำหรับนักพัฒนาที่ต้องทำงานกับระบบฐานข้อมูลขนาดใหญ่ การเข้าใจและใช้กลไก Promise อย่างเชี่ยวชาญ จึงเป็นทักษะสำคัญที่ขาดไม่ได้ในการเป็น Senior Architect อย่างแท้จริง