ไทย

เรียนรู้วิธีใช้ AbortController ของ JavaScript เพื่อยกเลิกการทำงานแบบ Asynchronous เช่น fetch requests, timers และอื่นๆ อย่างมีประสิทธิภาพ เพื่อให้โค้ดสะอาดและมีประสิทธิภาพมากขึ้น

JavaScript AbortController: การจัดการการยกเลิกการทำงานแบบ Asynchronous อย่างมืออาชีพ

ในการพัฒนาเว็บสมัยใหม่ การทำงานแบบอะซิงโครนัสเป็นสิ่งที่พบเห็นได้ทั่วไป การดึงข้อมูลจาก API, การตั้งเวลา และการจัดการกับการโต้ตอบของผู้ใช้ มักเกี่ยวข้องกับโค้ดที่ทำงานอย่างอิสระและอาจใช้เวลานาน อย่างไรก็ตาม มีบางสถานการณ์ที่คุณจำเป็นต้องยกเลิกการดำเนินการเหล่านี้ก่อนที่มันจะเสร็จสมบูรณ์ นี่คือจุดที่ AbortController interface ใน JavaScript เข้ามาช่วย มันเป็นวิธีที่สะอาดและมีประสิทธิภาพในการส่งสัญญาณขอยกเลิกไปยัง DOM operations และงานอะซิงโครนัสอื่นๆ

ทำความเข้าใจถึงความจำเป็นในการยกเลิก

ก่อนที่จะลงลึกในรายละเอียดทางเทคนิค เรามาทำความเข้าใจกันก่อนว่าทำไมการยกเลิกการทำงานแบบอะซิงโครนัสจึงมีความสำคัญ ลองพิจารณาสถานการณ์ทั่วไปเหล่านี้:

แนะนำ AbortController และ AbortSignal

อินเทอร์เฟซ AbortController ถูกออกแบบมาเพื่อแก้ปัญหาการยกเลิกการทำงานแบบอะซิงโครนัส ประกอบด้วยสองส่วนประกอบสำคัญ:

การใช้งานพื้นฐาน: การยกเลิก Fetch Requests

มาเริ่มด้วยตัวอย่างง่ายๆ ของการยกเลิก fetch request:


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

fetch('https://api.example.com/data', { signal })
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    return response.json();
  })
  .then(data => {
    console.log('Data:', data);
  })
  .catch(error => {
    if (error.name === 'AbortError') {
      console.log('Fetch aborted');
    } else {
      console.error('Fetch error:', error);
    }
  });

// To cancel the fetch request:
controller.abort();

คำอธิบาย:

  1. เราสร้าง instance ของ AbortController
  2. เราดึง AbortSignal ที่เกี่ยวข้องมาจาก controller
  3. เราส่ง signal ไปยัง options ของ fetch
  4. หากเราต้องการยกเลิก request เราจะเรียกใช้ controller.abort()
  5. ในบล็อก .catch() เราตรวจสอบว่า error เป็น AbortError หรือไม่ ถ้าใช่ เราจะรู้ว่า request ถูกยกเลิกแล้ว

การจัดการ AbortError

เมื่อ controller.abort() ถูกเรียก, fetch request จะถูกปฏิเสธ (rejected) ด้วย AbortError การจัดการ error นี้อย่างเหมาะสมในโค้ดของคุณเป็นสิ่งสำคัญอย่างยิ่ง การไม่ทำเช่นนั้นอาจนำไปสู่ unhandled promise rejections และพฤติกรรมที่ไม่คาดคิด

นี่คือตัวอย่างที่แข็งแกร่งขึ้นพร้อมการจัดการ error:


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

async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data', { signal });
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    const data = await response.json();
    console.log('Data:', data);
    return data;
  } catch (error) {
    if (error.name === 'AbortError') {
      console.log('Fetch aborted');
      return null; // Or throw the error to be handled further up
    } else {
      console.error('Fetch error:', error);
      throw error; // Re-throw the error to be handled further up
    }
  }
}

fetchData();

// To cancel the fetch request:
controller.abort();

แนวทางปฏิบัติที่ดีที่สุดสำหรับการจัดการ AbortError:

การยกเลิก Timers ด้วย AbortSignal

AbortSignal ยังสามารถใช้เพื่อยกเลิก timers ที่สร้างด้วย setTimeout หรือ setInterval ได้ด้วย ซึ่งต้องมีการทำงานด้วยตนเองเพิ่มขึ้นเล็กน้อย เนื่องจากฟังก์ชัน timer ที่มีอยู่แล้วไม่รองรับ AbortSignal โดยตรง คุณต้องสร้างฟังก์ชันแบบกำหนดเองที่คอยฟัง abort signal และล้าง timer เมื่อมันถูกเรียกใช้


function cancellableTimeout(callback, delay, signal) {
  let timeoutId;

  const timeoutPromise = new Promise((resolve, reject) => {
    timeoutId = setTimeout(() => {
      resolve(callback());
    }, delay);

    signal.addEventListener('abort', () => {
      clearTimeout(timeoutId);
      reject(new Error('Timeout Aborted'));
    });
  });

  return timeoutPromise;
}

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


cancellableTimeout(() => {
  console.log('Timeout executed');
}, 2000, signal)
.then(() => console.log("Timeout finished successfully"))
.catch(err => console.log(err));

// To cancel the timeout:
controller.abort();

คำอธิบาย:

  1. ฟังก์ชัน cancellableTimeout รับ callback, delay, และ AbortSignal เป็นอาร์กิวเมนต์
  2. มันจะตั้งค่า setTimeout และเก็บ ID ของ timeout ไว้
  3. มันจะเพิ่ม event listener ไปยัง AbortSignal เพื่อคอยฟังเหตุการณ์ abort
  4. เมื่อเหตุการณ์ abort ถูกกระตุ้น, event listener จะล้าง timeout และปฏิเสธ promise

การยกเลิก Event Listeners

เช่นเดียวกับ timers คุณสามารถใช้ AbortSignal เพื่อยกเลิก event listeners ได้ ซึ่งมีประโยชน์อย่างยิ่งเมื่อคุณต้องการลบ event listeners ที่เกี่ยวข้องกับ component ที่กำลังจะถูก unmount


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

const button = document.getElementById('myButton');

button.addEventListener('click', () => {
  console.log('Button clicked!');
}, { signal });

// To cancel the event listener:
controller.abort();

คำอธิบาย:

  1. เราส่ง signal เป็น option ไปยังเมธอด addEventListener
  2. เมื่อ controller.abort() ถูกเรียก, event listener จะถูกลบออกโดยอัตโนมัติ

AbortController ใน React Components

ใน React คุณสามารถใช้ AbortController เพื่อยกเลิกการทำงานแบบอะซิงโครนัสเมื่อ component ถูก unmount นี่เป็นสิ่งจำเป็นเพื่อป้องกันหน่วยความจำรั่วไหลและข้อผิดพลาดที่เกิดจากการอัปเดต component ที่ถูก unmount ไปแล้ว นี่คือตัวอย่างการใช้ useEffect hook:


import React, { useState, useEffect } from 'react';

function MyComponent() {
  const [data, setData] = useState(null);

  useEffect(() => {
    const controller = new AbortController();
    const signal = controller.signal;

    async function fetchData() {
      try {
        const response = await fetch('https://api.example.com/data', { signal });
        if (!response.ok) {
          throw new Error(`HTTP error! Status: ${response.status}`);
        }
        const data = await response.json();
        setData(data);
      } catch (error) {
        if (error.name === 'AbortError') {
          console.log('Fetch aborted');
        } else {
          console.error('Fetch error:', error);
        }
      }
    }

    fetchData();

    return () => {
      controller.abort(); // Cancel the fetch request when the component unmounts
    };
  }, []); // Empty dependency array ensures this effect runs only once on mount

  return (
    
{data ? (

Data: {JSON.stringify(data)}

) : (

Loading...

)}
); } export default MyComponent;

คำอธิบาย:

  1. เราสร้าง AbortController ภายใน useEffect hook
  2. เราส่ง signal ไปยัง fetch request
  3. เราคืนค่าฟังก์ชัน cleanup จาก useEffect hook ฟังก์ชันนี้จะถูกเรียกเมื่อ component ถูก unmount
  4. ภายในฟังก์ชัน cleanup เราเรียก controller.abort() เพื่อยกเลิก fetch request

กรณีการใช้งานขั้นสูง

การเชื่อมต่อ AbortSignals

บางครั้ง คุณอาจต้องการเชื่อมต่อ AbortSignal หลายๆ ตัวเข้าด้วยกัน ตัวอย่างเช่น คุณอาจมี parent component ที่ต้องการยกเลิกการทำงานใน child components ของมัน คุณสามารถทำได้โดยการสร้าง AbortController ใหม่และส่ง signal ของมันไปยังทั้ง parent และ child components

การใช้ AbortController กับไลบรารีของบุคคลที่สาม

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

ประโยชน์ของการใช้ AbortController

ความเข้ากันได้ของเบราว์เซอร์

AbortController ได้รับการสนับสนุนอย่างกว้างขวางในเบราว์เซอร์สมัยใหม่ รวมถึง Chrome, Firefox, Safari และ Edge คุณสามารถตรวจสอบตารางความเข้ากันได้บน MDN Web Docs สำหรับข้อมูลล่าสุด

Polyfills

สำหรับเบราว์เซอร์รุ่นเก่าที่ไม่รองรับ AbortController โดยกำเนิด คุณสามารถใช้ polyfill ได้ polyfill คือโค้ดชิ้นหนึ่งที่ให้ฟังก์ชันการทำงานของคุณสมบัติใหม่ๆ ในเบราว์เซอร์รุ่นเก่า มี AbortController polyfills หลายตัวให้เลือกใช้ออนไลน์

สรุป

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

แหล่งข้อมูลเพิ่มเติม