עברית

למדו כיצד להשתמש ב-AbortController של JavaScript לביטול יעיל של פעולות אסינכרוניות כמו בקשות fetch, טיימרים ועוד, להשגת קוד נקי ובעל ביצועים גבוהים יותר.

JavaScript AbortController: שליטה בביטול פעולות אסינכרוניות

בפיתוח ווב מודרני, פעולות אסינכרוניות הן דבר שבשגרה. שליפת נתונים מ-API, הגדרת טיימרים וטיפול באינטראקציות משתמש כרוכים לעתים קרובות בקוד שרץ באופן עצמאי ולעתים למשך זמן ממושך. עם זאת, ישנם תרחישים שבהם נדרש לבטל פעולות אלו לפני שהן מסתיימות. כאן נכנס לתמונה הממשק AbortController ב-JavaScript. הוא מספק דרך נקייה ויעילה לאותת בקשות ביטול לפעולות DOM ולמשימות אסינכרוניות אחרות.

הבנת הצורך בביטול

לפני שנצלול לפרטים הטכניים, בואו נבין מדוע ביטול פעולות אסינכרוניות הוא חשוב. קחו בחשבון את התרחישים הנפוצים הבאים:

הכירו את AbortController ו-AbortSignal

הממשק AbortController תוכנן לפתור את בעיית ביטול הפעולות האסינכרוניות. הוא מורכב משני רכיבים עיקריים:

שימוש בסיסי: ביטול בקשות Fetch

נתחיל עם דוגמה פשוטה לביטול בקשת fetch:


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. אנו יוצרים מופע של AbortController.
  2. אנו מקבלים את ה-AbortSignal המשויך מה-controller.
  3. אנו מעבירים את ה-signal לאפשרויות של fetch.
  4. אם אנו צריכים לבטל את הבקשה, אנו קוראים ל-controller.abort().
  5. בבלוק ה-.catch(), אנו בודקים אם השגיאה היא מסוג AbortError. אם כן, אנו יודעים שהבקשה בוטלה.

טיפול ב-AbortError

כאשר קוראים ל-controller.abort(), בקשת ה-fetch תידחה עם שגיאת AbortError. חשוב מאוד לטפל בשגיאה זו כראוי בקוד שלכם. אי טיפול יכול להוביל לדחיות הבטחה (promise rejections) לא מטופלות ולהתנהגות בלתי צפויה.

הנה דוגמה חזקה יותר עם טיפול בשגיאות:


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:

ביטול טיימרים עם AbortSignal

ניתן להשתמש ב-AbortSignal גם כדי לבטל טיימרים שנוצרו עם setTimeout או setInterval. זה דורש קצת יותר עבודה ידנית, מכיוון שפונקציות הטיימר המובנות אינן תומכות ישירות ב-AbortSignal. עליכם ליצור פונקציה מותאמת אישית שמאזינה לאות הביטול ומנקה את הטיימר כאשר הוא מופעל.


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 ושומרת את מזהה הטיימר.
  3. היא מוסיפה מאזין אירועים (event listener) ל-AbortSignal המאזין לאירוע abort.
  4. כאשר אירוע ה-abort מופעל, מאזין האירועים מנקה את הטיימר ודוחה את ההבטחה (promise).

ביטול מאזיני אירועים (Event Listeners)

בדומה לטיימרים, ניתן להשתמש ב-AbortSignal כדי לבטל מאזיני אירועים. זה שימושי במיוחד כאשר רוצים להסיר מאזיני אירועים המשויכים לקומפוננטה שמוסרת.


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 כאפשרות למתודה addEventListener.
  2. כאשר קוראים ל-controller.abort(), מאזין האירועים יוסר באופן אוטומטי.

AbortController בקומפוננטות React

ב-React, ניתן להשתמש ב-AbortController כדי לבטל פעולות אסינכרוניות כאשר קומפוננטה מוסרת. זה חיוני למניעת דליפות זיכרון ושגיאות הנגרמות מעדכון קומפוננטות שהוסרו. הנה דוגמה המשתמשת ב-hook בשם useEffect:


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 בתוך ה-hook useEffect.
  2. אנו מעבירים את ה-signal לבקשת ה-fetch.
  3. אנו מחזירים פונקציית ניקוי (cleanup function) מה-hook useEffect. פונקציה זו תיקרא כאשר הקומפוננטה מוסרת.
  4. בתוך פונקציית הניקוי, אנו קוראים ל-controller.abort() כדי לבטל את בקשת ה-fetch.

תרחישי שימוש מתקדמים

שירשור AbortSignals

לפעמים, ייתכן שתרצו לשרשר מספר AbortSignal-ים יחד. לדוגמה, ייתכן שיש לכם קומפוננטת אב שצריכה לבטל פעולות בקומפוננטות הבן שלה. ניתן להשיג זאת על ידי יצירת AbortController חדש והעברת האות שלו הן לקומפוננטת האב והן לקומפוננטות הבן.

שימוש ב-AbortController עם ספריות צד-שלישי

אם אתם משתמשים בספריית צד-שלישי שאינה תומכת ישירות ב-AbortSignal, ייתכן שתצטרכו להתאים את הקוד שלכם כדי לעבוד עם מנגנון הביטול של הספרייה. זה עשוי לכלול עטיפה של הפונקציות האסינכרוניות של הספרייה בפונקציות משלכם שמטפלות ב-AbortSignal.

היתרונות של שימוש ב-AbortController

תאימות דפדפנים

AbortController נתמך באופן נרחב בדפדפנים מודרניים, כולל Chrome, Firefox, Safari ו-Edge. ניתן לבדוק את טבלת התאימות ב-MDN Web Docs לקבלת המידע העדכני ביותר.

Polyfills

עבור דפדפנים ישנים יותר שאינם תומכים באופן טבעי ב-AbortController, ניתן להשתמש ב-polyfill. פוליפיל הוא קטע קוד המספק פונקציונליות של תכונה חדשה יותר בדפדפנים ישנים. ישנם מספר פוליפילים ל-AbortController זמינים באינטרנט.

סיכום

הממשק AbortController הוא כלי רב עוצמה לניהול פעולות אסינכרוניות ב-JavaScript. באמצעות AbortController, תוכלו לכתוב קוד נקי יותר, בעל ביצועים טובים יותר וחזק יותר, המטפל בביטולים בצורה אלגנטית. בין אם אתם שולפים נתונים מ-API, מגדירים טיימרים או מנהלים מאזיני אירועים, AbortController יכול לעזור לכם לשפר את האיכות הכוללת של יישומי הווב שלכם.

קריאה נוספת