ป้ายกำกับ: Promise

TypeScript: ใช้งาน Promise ที่ทำงานเสร็จเป็นลำดับแรกTypeScript: ใช้งาน Promise ที่ทำงานเสร็จเป็นลำดับแรก

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

อย่างไรก็ตาม ปัญหาที่ซับซ้อนกว่าการรู้ว่าข้อมูลจะพร้อมเมื่อไหร่ คือการจัดการ ลำดับขั้นตอน หากเรามีชุดงานที่ต้องทำตามคิวอย่างเคร่งครัด เช่น ต้องดึง User ID ก่อน แล้วจึงนำ ID นั้นไปค้นหา Profile ต่อ นี่คือกรณีที่เราต้องการ Sequential Execution หรือ Promises ที่ทำงานเสร็จเป็นลำดับอย่างแน่นอน

หลักการ: Sequential vs. Parallel Execution

การทำความเข้าใจความแตกต่างระหว่างการทำงานแบบคู่ขนานและการทำงานแบบเรียงลำดับเป็นสิ่งสำคัญ:

  • Parallel (คู่ขนาน): คือการเริ่มดำเนินการทั้งหมดพร้อมกัน เหมาะสำหรับภาระหน้าที่ที่ไม่เกี่ยวข้องกันเลย เราจะใช้ Promise.all() ในการรันชุดงานเหล่านี้พร้อมกันเพื่อประหยัดเวลา
  • Sequential (เรียงลำดับ): คือการรอให้ Task A เสร็จสมบูรณ์และได้ผลลัพธ์ก่อน จึงจะนำผลลัพธ์นั้นไปใช้เป็น Input สำหรับ Task B ต่อไป การทำงานลักษณะนี้ต้องใช้ await ใน TypeScript เพื่อบังคับให้โค้ดทำงานทีละขั้นตอน

การใช้งาน Sequential Promises ด้วย TypeScript และ PostgreSQL

ในทางปฏิบัติ เมื่อเราใช้ TypeScript ในการเรียกใช้ฟังก์ชันฐานข้อมูล (เช่น ผ่าน ORM หรือไลบรารี PostgreSQL Client) เราจะใช้โครงสร้าง async/await เพื่อให้โค้ดอ่านง่ายและมั่นใจได้ว่าขั้นตอนต่างๆ จะถูกรันตามลำดับที่กำหนด

สมมติว่าเราต้องการดึงข้อมูลสินค้าที่ต้องผ่าน 3 ขั้นตอน: 1) หา Category ID, 2) ใช้ ID นั้นหา Product List, และ 3) ใช้ Product List นั้นนับยอดรวม

แม้ว่าโค้ดหลักจะเป็น TypeScript แต่การดำเนินการฐานข้อมูลจะถูกส่งไปยัง PostgreSQL ซึ่งเราจะจำลองการทำงานแบบลำดับขั้นตอนโดยใช้ฟังก์ชัน PL/pgSQL:

SQL

-- ฟังก์ชันที่จำลองการทำงานแบบเรียงลำดับ (Sequential Task) ใน PostgreSQL
CREATE OR REPLACE FUNCTION get_sequential_data(p_input_id TEXT) RETURNS TEXT AS $$
DECLARE
    v_category_id INT;
    v_product_list TEXT;
    v_total_count INT;
BEGIN
    -- Task 1: ดึง Category ID (ต้องทำก่อน)
    SELECT category_id INTO v_category_id FROM categories WHERE id = p_input_id;
    
    -- Task 2: ใช้ Category ID เพื่อหา Product List (ต้องรอ Task 1)
    SELECT array_to_string(ARRAY(SELECT product_name FROM products WHERE category_id = v_category_id), ',')
    INTO v_product_list;

    -- Task 3: ใช้ Product List เพื่อนับจำนวนสินค้า (ต้องรอ Task 2)
    SELECT count(*) INTO v_total_count FROM products WHERE category_id = v_category_id;

    RETURN 'Success: Products found: ' || v_product_list || ', Total count: ' || v_total_count;
END;
$$ LANGUAGE plpgsql;

-- การเรียกใช้ฟังก์ชันนี้ใน TypeScript จะถูก await เพื่อให้แน่ใจว่าขั้นตอนทั้งหมดเสร็จสมบูรณ์ตามลำดับ
-- ตัวอย่างการเรียกใช้: await db.query('SELECT * FROM get_sequential_data($1)', ['1']);

บทสรุปและข้อควรจำ

การใช้ async/await ใน TypeScript คือเครื่องมือที่ทรงพลังที่สุดในการจัดการ Promise ที่ต้องทำงานเป็นลำดับ (Sequential) เพราะมันช่วยให้โค้ดที่ซับซ้อนดูเหมือนการทำงานแบบ Synchronous ทำให้ง่ายต่อการ Debug และการบำรุงรักษา

จงจำไว้ว่า:

  • ถ้า Task A ไม่จำเป็นต้องใช้ผลลัพธ์ของ Task B ให้ใช้ Promise.all() (Parallel) เพื่อความเร็วสูงสุด
  • ถ้า Task B ต้องใช้ผลลัพธ์ของ Task A เสมอ ให้ใช้ await (Sequential) เพื่อความถูกต้องของข้อมูล