PlusMagi's Blog By Pitt Phunsanit Database Asynchronous JavaScript ยุคใหม่: การใช้งาน Promises และ Async/Await แทน Callback Hell

Asynchronous JavaScript ยุคใหม่: การใช้งาน Promises และ Async/Await แทน Callback Hell

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

หากเราจัดการการไหลของข้อมูลเหล่านี้อย่างไม่เป็นระบบ อาจนำไปสู่ปัญหาที่เรียกว่า ‘Callback Hell’ ในบริบทของฐานข้อมูล นั่นคือการเขียนโค้ดที่ซับซ้อนและซ้อนกันเป็นชั้นๆ ทำให้การอ่าน การแก้ไข และการจัดการข้อผิดพลาด (Error Handling) เป็นไปได้ยากยิ่ง


หลักการทำงาน: การเลียนแบบ Async/Await ใน PostgreSQL

แนวคิดของ Promises และ Async/Await ใน JavaScript คือการทำให้โค้ด Asynchronous มีโครงสร้างที่อ่านง่ายและเป็นเส้นตรง (Linear Flow) ซึ่งหมายความว่าเราสามารถเขียนโค้ดที่ดูเหมือนกับการทำงานแบบ Synchronous ทั่วไป แต่ยังคงสามารถจัดการกับงานที่ต้องรอผลลัพธ์ได้

ใน PostgreSQL เราสามารถบรรลุแนวคิดนี้ได้โดยการใช้ภาษาเชิงกระบวนการ (Procedural Language) เช่น PL/pgSQL หรือการออกแบบ Transaction ที่มีการควบคุมลำดับขั้นตอนอย่างเคร่งครัด เพื่อให้มั่นใจว่าการดำเนินการแต่ละขั้นตอนจะเสร็จสิ้นและส่งผลลัพธ์ที่ถูกต้องก่อนที่ขั้นตอนถัดไปจะเริ่มขึ้น ซึ่งช่วยลดความซับซ้อนในการจัดการ Flow ได้อย่างมาก

ตัวอย่าง: การประมวลผลข้อมูลแบบลำดับขั้นตอน (Sequential Data Processing)

สมมติว่าเราต้องการประมวลผลรายการสินค้า (item list) โดยต้องมีการตรวจสอบความถูกต้อง (Validation) และการแปลงข้อมูล (Transformation) อย่างเป็นลำดับก่อนที่จะบันทึกผลลัพธ์ การใช้ PL/pgSQL จะช่วยให้เราสามารถจำลอง Flow ที่เป็นเส้นตรงและจัดการข้อผิดพลาดได้ง่ายกว่าการใช้ชุดคำสั่ง SQL ที่ซับซ้อนและซ้อนกัน

SQL

-- สร้างตารางจำลองสำหรับเก็บผลลัพธ์ที่ผ่านการประมวลผลแล้ว (Processed_Results)
CREATE TABLE IF NOT EXISTS processed_results (
    id SERIAL PRIMARY KEY,
    processed_data TEXT,
    processed_at TIMESTAMP DEFAULT NOW()
);

-- ใช้ DO block เพื่อจำลองการทำงานแบบ Async/Await ที่เป็นลำดับขั้นตอน
DO $$
DECLARE
    item_list TEXT := 'apple,banana,orange';
    processed_data TEXT := '';
BEGIN
    -- Step 1: Simulate initial validation/start (Initial Promise)
    RAISE NOTICE '--- เริ่มต้นการประมวลผลข้อมูลรายการ: % ---', item_list;

    -- Step 2: Process sequentially (Simulating await)
    FOR item IN unnest(string_to_array(item_list, ',')) LOOP
        -- การทำงานแต่ละรอบต้องเสร็จก่อนจึงจะไปรอบถัดไป (Sequential Dependency)
        processed_data := processed_data || item || ' | ';
    END LOOP;

    -- Step 3: Final action (The Resolve)
    INSERT INTO processed_results (processed_data) VALUES (processed_data);
    RAISE NOTICE '✅ การประมวลผลเสร็จสมบูรณ์และบันทึกผลลัพธ์เรียบร้อยแล้ว';
END $$ LANGUAGE plpgsql;

บทสรุป: การออกแบบระบบที่ทนทาน

การทำความเข้าใจแนวคิดของการไหลของข้อมูลแบบ Asynchronous ไม่ได้จำกัดอยู่แค่ภาษา JavaScript เท่านั้น แต่เป็นหลักการออกแบบระบบที่สำคัญที่ทุกระบบฐานข้อมูลต้องคำนึงถึง การใช้ PL/pgSQL หรือการออกแบบ Transaction ที่มีลำดับขั้นตอนที่ชัดเจน ช่วยให้เราสามารถยกระดับโค้ดฐานข้อมูลให้มีความเป็นระเบียบ (Maintainable) และเชื่อถือได้ (Reliable) เหมือนกับการใช้ `async`/`await` ทำให้การจัดการข้อมูลที่ซับซ้อนเป็นเรื่องที่ง่ายและเป็นเส้นตรงมากขึ้น