ใช้การจัดการข้อผิดพลาดเมื่อใช้ Fetch API

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

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

ป้องกันข้อผิดพลาดที่อาจเกิดขึ้นกับเครือข่าย

ส่วนนี้อธิบายสถานการณ์ที่ผู้ใช้สร้างวิดีโอใหม่ชื่อ "My Travels.mp4" และพยายามอัปโหลดวิดีโอไปยังเว็บไซต์แชร์วิดีโอ

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

ตัวอย่างข้อผิดพลาดของผู้ใช้

  • ผู้ใช้อัปโหลดไฟล์ภาพ (เช่น JPEG) แทนไฟล์วิดีโอ
  • ผู้ใช้เริ่มอัปโหลดไฟล์วิดีโอที่ไม่ถูกต้อง จากนั้น ผู้ใช้ระบุไฟล์วิดีโอที่ถูกต้องสำหรับการอัปโหลดในระหว่างการอัปโหลด
  • ผู้ใช้คลิก "ยกเลิกการอัปโหลด" โดยไม่ได้ตั้งใจขณะกำลังอัปโหลด

ตัวอย่างการเปลี่ยนแปลงด้านสิ่งแวดล้อม

  • การเชื่อมต่ออินเทอร์เน็ตจะออฟไลน์ในระหว่างอัปโหลดวิดีโอ
  • เบราว์เซอร์จะรีสตาร์ทขณะอัปโหลดวิดีโอ
  • เซิร์ฟเวอร์ของเว็บไซต์แชร์วิดีโอจะรีสตาร์ทขณะอัปโหลดวิดีโอ

ตัวอย่างข้อผิดพลาดของเว็บไซต์แชร์วิดีโอ

  • เว็บไซต์แชร์วิดีโอจัดการชื่อไฟล์ด้วยการเว้นวรรคไม่ได้ แทนที่จะใช้ "My Travels.mp4" ระบบจะใช้ชื่ออย่างเช่น "My_Travels.mp4" หรือ "MyTravels.mp4"
  • เว็บไซต์แชร์วิดีโอไม่สามารถอัปโหลดวิดีโอที่มีขนาดเกินขนาดไฟล์สูงสุดที่ยอมรับได้
  • เว็บไซต์แชร์วิดีโอไม่รองรับตัวแปลงรหัสวิดีโอในวิดีโอที่อัปโหลด

ตัวอย่างเหล่านี้สามารถเกิดขึ้นได้ในชีวิตจริง คุณอาจเคยเห็นตัวอย่างเช่นนี้มาก่อน เราจะเลือกตัวอย่างหนึ่งตัวอย่างจากแต่ละหมวดหมู่ก่อนหน้านี้ และพูดถึงประเด็นต่อไปนี้

  • ลักษณะการทำงานเริ่มต้นคืออะไรหากบริการแชร์วิดีโอจัดการตัวอย่างที่ระบุไม่ได้
  • ผู้ใช้คาดหวังว่าจะเกิดอะไรขึ้นในตัวอย่างนี้
  • เราจะปรับปรุงกระบวนการนี้ได้อย่างไร
การดำเนินการ ผู้ใช้เริ่มอัปโหลดไฟล์วิดีโอที่ไม่ถูกต้อง จากนั้น ผู้ใช้ระบุไฟล์วิดีโอที่ถูกต้องสำหรับการอัปโหลดในระหว่างการอัปโหลด
สิ่งที่จะเกิดขึ้นโดยค่าเริ่มต้น ไฟล์ต้นฉบับจะยังคงอัปโหลดต่อไปในเบื้องหลังขณะอัปโหลดไฟล์ใหม่พร้อมกัน
สิ่งที่ผู้ใช้คาดหวัง ผู้ใช้คาดหวังให้การอัปโหลดเดิมหยุดลง เพื่อที่จะไม่สูญเสียแบนด์วิดท์อินเทอร์เน็ตเพิ่มเติม
สิ่งที่ปรับปรุงได้ JavaScript จะยกเลิกคำขอดึงข้อมูลสำหรับไฟล์ต้นฉบับก่อนที่ไฟล์ใหม่จะเริ่มอัปโหลด
การดำเนินการ ผู้ใช้สูญเสียการเชื่อมต่ออินเทอร์เน็ตในระหว่างการอัปโหลดวิดีโอ
สิ่งที่จะเกิดขึ้นโดยค่าเริ่มต้น ดูเหมือนว่าแถบความคืบหน้าการอัปโหลดจะค้างอยู่ที่ 50% แต่สุดท้ายแล้ว Fetch API จะหมดเวลาและข้อมูลที่อัปโหลดจะถูกทิ้งไป เมื่อการเชื่อมต่ออินเทอร์เน็ตกลับมา ผู้ใช้ต้องอัปโหลดไฟล์อีกครั้ง
สิ่งที่ผู้ใช้คาดหวัง ผู้ใช้คาดหวังว่าจะได้รับการแจ้งเตือนเมื่ออัปโหลดไฟล์ไม่ได้ และคาดว่าการอัปโหลดจะกลับมาทำงานอีกครั้งโดยอัตโนมัติที่ 50% เมื่อกลับมาออนไลน์
สิ่งที่ปรับปรุงได้ หน้าอัปโหลดจะแจ้งให้ผู้ใช้ทราบถึงปัญหาการเชื่อมต่ออินเทอร์เน็ต และให้ความมั่นใจแก่ผู้ใช้ว่าระบบจะอัปโหลดต่อเมื่อการเชื่อมต่ออินเทอร์เน็ตกลับมาทำงานอีกครั้ง
การดำเนินการ เว็บไซต์แชร์วิดีโอจัดการชื่อไฟล์ด้วยการเว้นวรรคไม่ได้ โดยให้ใช้ชื่ออย่างเช่น "My_Travels.mp4" หรือ "MyTravels.mp4" แทน "My Travels.mp4"
สิ่งที่จะเกิดขึ้นโดยค่าเริ่มต้น ผู้ใช้ต้องรอให้การอัปโหลดเสร็จสมบูรณ์ เมื่ออัปโหลดไฟล์แล้ว และแถบความคืบหน้าอ่านว่า "100%" แถบความคืบหน้าจะแสดงข้อความว่า "โปรดลองอีกครั้ง"
สิ่งที่ผู้ใช้คาดหวัง ผู้ใช้คาดหวังว่าจะต้องแจ้งข้อจำกัดของชื่อไฟล์ก่อนการอัปโหลด หรืออย่างน้อยภายในวินาทีแรกของการอัปโหลด
สิ่งที่ปรับปรุงได้ โดยหลักการแล้ว บริการแชร์วิดีโอจะสนับสนุนชื่อไฟล์ที่มีการเว้นวรรค ทั้งนี้ ตัวเลือกอื่นๆ คือการแจ้งให้ผู้ใช้ทราบถึงข้อจํากัดของชื่อไฟล์ก่อนที่จะเริ่มอัปโหลด หรือบริการแชร์วิดีโอควรปฏิเสธการอัปโหลดพร้อมด้วยข้อความแสดงข้อผิดพลาดโดยละเอียด

จัดการข้อผิดพลาดด้วย Fetch API

โปรดทราบว่าตัวอย่างโค้ดต่อไปนี้ใช้awaitระดับบนสุด (การรองรับเบราว์เซอร์) เนื่องจากฟีเจอร์นี้ช่วยลดความซับซ้อนของโค้ดได้

เมื่อ Fetch API แสดงข้อผิดพลาด

ตัวอย่างนี้ใช้คำสั่ง try/catch Block เพื่อตรวจหาข้อผิดพลาดใดก็ตามที่อยู่ภายในบล็อก try ตัวอย่างเช่น หาก Fetch API ดึงข้อมูลทรัพยากรที่ระบุไม่ได้ ระบบจะแสดงข้อผิดพลาด ภายใน catch บล็อกเช่นนี้ โปรดพยายามมอบประสบการณ์ของผู้ใช้ที่มีความหมาย หากไอคอนหมุนเป็นอินเทอร์เฟซผู้ใช้ทั่วไปที่แสดงถึงความคืบหน้าบางอย่าง แสดงต่อผู้ใช้ คุณสามารถดำเนินการต่อไปนี้ภายในบล็อก catch

  1. นำไอคอนหมุนออกจากหน้า
  2. ส่งข้อความที่มีประโยชน์ซึ่งอธิบายถึงข้อผิดพลาดและตัวเลือกที่ผู้ใช้สามารถทำได้
  3. แสดงปุ่ม "ลองอีกครั้ง" ต่อผู้ใช้ตามตัวเลือกที่มี
  4. ส่งรายละเอียดข้อผิดพลาดไปยังบริการติดตามข้อผิดพลาดหรือส่งแบ็กเอนด์ไปยังเบื้องหลัง การดำเนินการนี้จะบันทึกข้อผิดพลาดเพื่อให้วินิจฉัยได้ในภายหลัง
try {
  const response = await fetch('https://website');
} catch (error) {
  // TypeError: Failed to fetch
  console.log('There was an error', error);
}

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

เมื่อรหัสสถานะเครือข่ายแสดงข้อผิดพลาด

ตัวอย่างโค้ดนี้จะส่งคำขอไปยังบริการทดสอบ HTTP ที่ตอบสนองด้วยรหัสสถานะ HTTP 429 Too Many Requests เสมอ ที่น่าสนใจคือการตอบกลับไปไม่ถึงบล็อก catch สถานะ 404 รวมถึงรหัสสถานะอื่นๆ จะไม่แสดงข้อผิดพลาดของเครือข่าย แต่จะแก้ไขตามปกติแทน

ในการตรวจสอบว่ารหัสสถานะ HTTP สำเร็จหรือไม่ คุณสามารถใช้ตัวเลือกใดๆ ต่อไปนี้

  • ใช้พร็อพเพอร์ตี้ Response.ok เพื่อระบุว่ารหัสสถานะอยู่ในช่วงตั้งแต่ 200 ถึง 299 หรือไม่
  • ใช้พร็อพเพอร์ตี้ Response.status เพื่อระบุว่าการตอบกลับสำเร็จหรือไม่
  • ใช้ข้อมูลเมตาอื่นๆ เช่น Response.headers เพื่อประเมินว่าการตอบกลับสำเร็จหรือไม่
let response;

try {
  response = await fetch('https://httpbin.org/status/429');
} catch (error) {
  console.log('There was an error', error);
}

// Uses the 'optional chaining' operator
if (response?.ok) {
  console.log('Use the response here!');
} else {
  console.log(`HTTP Response Code: ${response?.status}`)
}

แนวทางปฏิบัติแนะนำคือการทำงานร่วมกับคนในองค์กรและทีมของคุณเพื่อทำความเข้าใจรหัสสถานะการตอบกลับ HTTP ที่อาจเกิดขึ้น บางครั้งนักพัฒนาแบ็กเอนด์ ฝ่ายปฏิบัติการของนักพัฒนา และวิศวกรบริการสามารถให้ข้อมูลเชิงลึกที่เฉพาะเจาะจงเกี่ยวกับกรณีปัญหาที่เป็นไปได้ซึ่งคุณอาจคาดไม่ถึง

เมื่อมีข้อผิดพลาดในการแยกวิเคราะห์การตอบสนองของเครือข่าย

ตัวอย่างโค้ดนี้แสดงข้อผิดพลาดอีกประเภทหนึ่งที่อาจเกิดขึ้นเมื่อแยกวิเคราะห์เนื้อหาการตอบกลับ อินเทอร์เฟซ Response มีวิธีที่สะดวกในการแยกวิเคราะห์ข้อมูลประเภทต่างๆ เช่น ข้อความหรือ JSON ในโค้ดต่อไปนี้ จะมีคำขอเครือข่ายไปยังบริการทดสอบ HTTP ที่แสดงสตริง HTML เป็นข้อความการตอบสนอง อย่างไรก็ตาม ระบบได้พยายามแยกวิเคราะห์เนื้อหาการตอบกลับเป็น JSON และมีข้อผิดพลาด

let json;

try {
  const response = await fetch('https://httpbin.org/html');
  json = await response.json();
} catch (error) {
  if (error instanceof SyntaxError) {
    // Unexpected token < in JSON
    console.log('There was a SyntaxError', error);
  } else {
    console.log('There was an error', error);
  }
}

if (json) {
  console.log('Use the JSON here!', json);
}

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

ลองพิจารณาสถานการณ์ต่อไปนี้: คุณมีทรัพยากรระยะไกลที่ส่งคืนการตอบกลับ JSON ที่ถูกต้องและแยกวิเคราะห์สำเร็จด้วยเมธอด Response.json() อาจมีบางครั้งที่บริการไม่ทำงาน เมื่อลงแล้ว ระบบจะแสดงผล 500 Internal Server Error หากไม่ได้ใช้เทคนิคการจัดการข้อผิดพลาดที่เหมาะสมในระหว่างการแยกวิเคราะห์ JSON ปัญหานี้อาจทำให้หน้าเว็บของผู้ใช้เสียหายได้เนื่องจากเกิดข้อผิดพลาดที่ไม่มีการจัดการ

เมื่อต้องยกเลิกคำขอเครือข่ายก่อนที่จะดำเนินการเสร็จสมบูรณ์

ตัวอย่างโค้ดนี้ใช้ AbortController เพื่อยกเลิกคำขอที่กำลังดำเนินอยู่ คำขอในการดำเนินการคือคำขอเครือข่ายที่เริ่มขึ้นแล้วแต่ยังไม่เสร็จสมบูรณ์

สถานการณ์ที่คุณอาจต้องยกเลิกคำขอที่ดำเนินการอยู่อาจแตกต่างกันไป แต่ท้ายที่สุดแล้วจะขึ้นอยู่กับกรณีการใช้งานและสภาพแวดล้อมของคุณ โค้ดต่อไปนี้แสดงวิธีส่ง AbortSignal ไปยัง Fetch API AbortSignal แนบอยู่กับ AbortController และ AbortController มีเมธอด abort() ซึ่งบอกให้เบราว์เซอร์ทราบว่าควรยกเลิกคำขอเครือข่าย

const controller = new AbortController();
const signal = controller.signal;

// Cancel the fetch request in 500ms
setTimeout(() => controller.abort(), 500);

try {
  const url = 'https://httpbin.org/delay/1';
  const response = await fetch(url, { signal });
  console.log(response);
} catch (error) {
  // DOMException: The user aborted a request.
  console.log('Error: ', error)
}

บทสรุป

แง่มุมหนึ่งที่สำคัญของการจัดการข้อผิดพลาดคือการกำหนดส่วนต่างๆ ที่อาจผิดพลาดได้ ในแต่ละสถานการณ์ โปรดตรวจสอบว่าคุณมีทางเลือกสำรองที่เหมาะสมสำหรับผู้ใช้ เกี่ยวกับคําขอดึงข้อมูล ให้ถามตัวเองคําถามต่อไปนี้ เช่น

  • จะเกิดอะไรขึ้นหากเซิร์ฟเวอร์เป้าหมายหยุดทำงาน
  • จะเกิดอะไรขึ้นหากการดึงข้อมูลได้รับการตอบสนองที่ไม่คาดคิด
  • จะเกิดอะไรขึ้นหากการเชื่อมต่ออินเทอร์เน็ตของผู้ใช้ล้มเหลว

คุณยังสามารถร่างโฟลว์ชาร์ตที่อธิบายฟังก์ชันและอินเทอร์เฟซผู้ใช้สําหรับสถานการณ์ต่างๆ ได้เช่นกัน ทั้งนี้ขึ้นอยู่กับความซับซ้อนของหน้าเว็บ