ป้ายกำกับ: Async/await

การทำ Request Timeout และการยกเลิก Request ใน Fetch API ด้วย AbortControllerการทำ Request Timeout และการยกเลิก Request ใน Fetch API ด้วย AbortController

ในโลกของการพัฒนาเว็บแอปพลิเคชันสมัยใหม่ การสื่อสารกับ API ภายนอกเป็นหัวใจหลักของการทำงาน แต่สิ่งที่นักพัฒนาต้องเผชิญอยู่เสมอคือปัญหาความล่าช้าของเครือข่าย (Network Latency) หรือการตอบสนองที่ไม่มีกำหนดเวลานั้นเป็นสิ่งที่หลีกเลี่ยงไม่ได้ การปล่อยให้โค้ดรอคำตอบจากเซิร์ฟเวอร์ไปเรื่อย ๆ โดยไม่มีกลไกควบคุม ไม่เพียงแต่ส่งผลเสียต่อประสบการณ์ผู้ใช้งาน (UX) แต่ยังทำให้ทรัพยากรของเบราว์เซอร์ถูกผูกไว้โดยไม่จำเป็น ดังนั้น การจัดการวงจรชีวิตของการเรียกข้อมูล (Request Lifecycle) อย่างมีประสิทธิภาพจึงเป็นทักษะสำคัญที่ Senior Developer ทุกคนต้องเชี่ยวชาญ

🔌 AbortController: หัวใจของการยกเลิก Request อย่างสง่างาม

หัวใจสำคัญในการจัดการ Request Timeout และการยกเลิกใน Fetch API คือการใช้ Web Standard ที่ทรงพลังอย่าง AbortController ก่อนหน้านี้ การทำงานกับ Promise ของ Fetch มักจะรอจนกว่าคำขอจะสำเร็จหรือล้มเหลวด้วยเหตุผลที่ชัดเจน แต่หากเกิดความล่าช้าเกินขีดจำกัดที่เรายอมรับได้ เราจำเป็นต้องมีกลไกในการ “ยกเลิก” (Cancel) คำขอนั้นอย่างสง่างาม (Graceful Cancellation) เพื่อป้องกันการค้างของ UI และการรั่วไหลของทรัพยากร

AbortController ทำหน้าที่เป็นตัวควบคุมสัญญาณ (Signal) ที่สามารถส่งคำสั่งยกเลิกไปยัง Promise หรือ API Call อื่น ๆ ได้อย่างแม่นยำ เมื่อเราสร้าง AbortController ขึ้นมา เราจะได้ทั้ง controller และ signal ซึ่ง signal นี้เองที่เราต้องส่งเข้าไปในอาร์กิวเมนต์ของ Fetch API เพื่อให้เบราว์เซอร์ทราบว่าคำขอนี้สามารถถูกยกเลิกได้เมื่อถึงเวลาที่กำหนด

💻 การใช้งานจริง: การตั้งค่า Timeout ด้วย AbortController

การจำลอง Timeout ใน JavaScript ไม่สามารถทำได้ด้วยการตั้งค่า Timeout บน Fetch API โดยตรง แต่เราสามารถใช้ AbortController ร่วมกับ setTimeout เพื่อสร้างกลไกการยกเลิกที่จำลองการหมดเวลาได้

JavaScript

// 1. สร้าง AbortController และ Signal<br>const controller = new AbortController();<br>const signal = controller.signal;<br><br>// 2. ตั้งค่า Timeout (เช่น 3 วินาที)<br>const timeoutId = setTimeout(() => {<br>  // เมื่อครบเวลา ให้เรียก abort() เพื่อยกเลิกคำขอ<br>  controller.abort();<br>}, 3000);<br><br>async function fetchDataWithTimeout() {<br>  try {<br>    // ส่ง signal เข้าไปใน Fetch API<br>    const response = await fetch('https://api.example.com/data', {<br>      signal: signal<br>    });<br>    const data = await response.json();<br>    console.log('Success:', data);<br>  } catch (error) {<br>    // ตรวจสอบว่าเกิดจากการยกเลิกหรือไม่<br>    if (error.name === 'AbortError') {<br>      console.error('Request was aborted due to timeout.');<br>    } else {<br>      console.error('An unexpected error occurred:', error);<br>    }<br>  } finally {<br>    // ต้องเคลียร์ setTimeout เสมอ เพื่อป้องกัน Memory Leak<br>    clearTimeout(timeoutId);<br>  }<br>}<br><br>fetchDataWithTimeout();

✨ แนวทางปฏิบัติที่ดีที่สุด (Best Practices)

  1. Always Clear Timeout: สิ่งสำคัญที่สุดคือการใช้ clearTimeout(timeoutId) ในบล็อก finally เสมอ เพื่อให้แน่ใจว่าแม้ว่าการเรียกข้อมูลจะสำเร็จก่อนที่ Timeout จะทำงาน ก็จะไม่เกิด Memory Leak จาก setTimeout ที่ค้างอยู่
  2. Error Handling: เมื่อเกิดการยกเลิก (Cancellation) Fetch API จะโยนข้อผิดพลาดที่มีชื่อว่า AbortError ออกมา ดังนั้นการใช้ try...catch และตรวจสอบ error.name === 'AbortError' จึงเป็นสิ่งจำเป็นในการแยกแยะระหว่างข้อผิดพลาดที่เกิดจาก Timeout กับข้อผิดพลาดของเครือข่ายจริง
  3. Resource Management: การใช้กลไกนี้ช่วยให้แอปพลิเคชันของคุณมีเสถียรภาพสูงขึ้นอย่างมาก เพราะคุณสามารถควบคุมวงจรชีวิตของทรัพยากรเครือข่ายได้อย่างชัดเจน

✅ บทสรุป: การเขียนโค้ดที่แข็งแกร่งและตอบสนอง

การทำความเข้าใจและนำ AbortController มาใช้ในการจัดการ Request Timeout ไม่ใช่แค่การแก้ไขบั๊ก แต่เป็นการยกระดับคุณภาพของโค้ดให้เป็นระดับ Senior Architect เพราะมันแสดงให้เห็นถึงความเข้าใจในเรื่องการจัดการทรัพยากร (Resource Management) และประสบการณ์ผู้ใช้งาน (UX) ที่ยอดเยี่ยม

ด้วยการใช้กลไกนี้ นักพัฒนาสามารถสร้างเว็บแอปพลิเคชันที่ตอบสนองได้อย่างรวดเร็วและเชื่อถือได้ แม้ในสภาวะเครือข่ายที่ผันผวน ทำให้โค้ดของคุณไม่เพียงแค่ทำงานได้ แต่ยังทำงานได้อย่างสง่างามและมีประสิทธิภาพสูงสุด