Haliviq
CLI

debounce ช่องค้นหาอย่างเดียวไม่พอ ต้องยกเลิก request ด้วย

ต่อให้ debounce แล้ว response ที่ช้าอาจมาถึงหลัง response ใหม่กว่า แล้วเขียนทับด้วยผลเก่า การสร้าง AbortController ต่อ request และยกเลิกอันก่อนหน้า ช่วยแก้บั๊กผลค้นหาเก่ากะพริบขึ้นมาของคนที่พิมพ์เร็ว และยังช่วยให้ server ไม่ต้องทำงานที่ไม่มีใครเห็นผลจนจบ

เกิดอะไรขึ้น

ช่องค้นหาของเรา debounce อยู่แล้ว แต่คนที่พิมพ์เร็วก็ยังเห็นผลลัพธ์กะพริบ บางครั้ง list แสดงผลของ query ก่อนหน้า หลังจากผลที่ถูกต้องขึ้นมาแล้วไม่นาน

debounce ทำงานถูกต้องตามที่ตั้งใจ ปัญหาอยู่ที่อื่น คือมีสอง request วิ่งอยู่พร้อมกัน และเสร็จไม่ตรงลำดับที่ส่ง

ทำไมถึงเป็นแบบนั้น

debounce ลดแค่จำนวน request ที่ส่ง ไม่ได้บอกอะไรเรื่องลำดับที่มันกลับมา ถ้าผู้ใช้หยุดพิมพ์หลัง "re" แล้วพิมพ์ต่อเป็น "react" ทั้งสอง request จะอยู่ในอากาศพร้อมกัน และ network หรือ server อาจตอบอันเก่าเป็นอันสุดท้ายก็ได้

response ไหนมาถึงทีหลังสุดจะชนะตอน set state ถ้าเป็นอันเก่า มันก็เขียนทับผลที่ใหม่กว่า นี่คือ race condition แบบคลาสสิก และ server ก็อาจยังทำงานกับ query ที่ไม่มีใครรอแล้ว

ตอนนี้เราทำอย่างไร

เราสร้าง AbortController ต่อหนึ่ง request และยกเลิกอันก่อนหน้าก่อนเริ่มอันใหม่ fetch ที่ถูกยกเลิกจะ reject ด้วย AbortError ซึ่งเราเมินไป ทำให้มีแค่ request ล่าสุดที่อัปเดต UI ได้

การยกเลิกยังช่วย backend ได้ถ้ามันสังเกตเห็นว่า connection ปิดแล้ว แต่จะประหยัดได้แค่ไหนขึ้นกับว่า server หยุดทำงานเมื่อ client ตัดการเชื่อมต่อหรือไม่ ต่อให้ไม่หยุด UI ก็ยังปลอดภัยจากข้อมูลเก่า

TypeScriptts
let controller: AbortController | undefined

async function search(query: string) {
  controller?.abort()
  controller = new AbortController()
  try {
    const res = await fetch(`/api/search?q=${encodeURIComponent(query)}`, {
      signal: controller.signal,
    })
    return await res.json()
  } catch (err) {
    if ((err as Error).name === 'AbortError') return undefined
    throw err
  }
}

เช็กลิสต์สั้น ๆ

  1. 1เก็บ ref ของ AbortController ตัวปัจจุบันไว้
  2. 2abort มันก่อนเริ่ม request ใหม่
  3. 3ส่ง signal ของมันให้ fetch
  4. 4เมิน error ที่ error.name เป็น AbortError
  5. 5abort ตอน unmount ด้วย response ที่มาช้าจะได้ไม่ set state

จำไว้สั้น ๆ

debounce จำกัดจำนวน request ที่ส่ง ส่วนการยกเลิกทำให้มีแค่อันล่าสุดที่ชนะได้

เราอยากฟังว่าคุณกำลังสร้างอะไรอยู่

คุยกับเรา