מה זה AbortController ואיך מבטלים בקשות אסינכרוניות ב-JavaScript

סער טויטו

סער טויטו

Software & Web Development


TL;DR
  • AbortController הוא ממשק מובנה שמאפשר לבטל בקשות רשת ופעולות אסינכרוניות ב-JavaScript בצורה סטנדרטית ונקייה.
  • AbortSignal הוא תעלת התקשורת מועבר ל-fetch כפרמטר, ומפעיל את הביטול ברגע שקוראים ל-controller.abort().
  • ניתן לבטל מספר בקשות עם controller אחד בו-זמנית — שימושי כשיש כמה בקשות תלויות שצריך לבטל יחד.
  • AbortSignal.timeout() מתודה סטטית חדשה שיוצרת signal שמבטל את הבקשה אוטומטית לאחר זמן מוגדר.

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

AbortController במבט כללי

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

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

AbortController ו-AbortSignal: איך הם עובדים יחד

AbortController מורכב משני חלקים עיקריים שעובדים יחד:

  • AbortController

    AbortController הוא האובייקט המרכזי המשמש ליצירת "בקר ביטול" (cancellation controller). הוא מספק את המתודה abort() שמאפשרת לשלוח אות ביטול (signal) לכל פעולה אסינכרונית שמאזינה לו, ולסמן שיש להפסיק את הביצוע.

  • AbortSignal

    AbortSignal הוא האובייקט שמתקבל מתוך ה-AbortController באמצעות controller.signal. את ה-signal הזה מעבירים לפונקציות שתומכות בביטול (כמו fetch), והוא משמש כתעלת תקשורת שמעדכנת את הפונקציה אם התרחש ביטול.

// Create a new instance of AbortController
const controller = new AbortController();

// Access its signal
const signal = controller.signal;

// Use the signal in a fetch request
fetch('https://api.example.com/data', { signal })
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(err => {
    if (err.name === 'AbortError') {
      console.log('הבקשה בוטלה!');
    } else {
      console.error('שגיאה אחרת:', err);
    }
  });

// Trigger the abort
controller.()abort;

איך AbortController עובד מאחורי הקלעים

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

  • אובייקט ה-Signal הוא EventTarget

    אובייקט ה-AbortSignal הוא למעשה instance של EventTarget, כלומר הוא מסוגל לשדר אירועים (כמו 'abort') ולתמוך בהאזנה לאירועים, בדומה לאובייקטים ב-DOM. תכונה זו מאפשרת לפונקציות אסינכרוניות להירשם לאירועי ביטול ולטפל בהם בצורה מסודרת.

  • שידור אירוע ה-abort

    כאשר קוראים למתודה controller.abort(), מתבצעות מספר פעולות:

    • הפרופרטי signal.aborted מתעדכנת ל-true
    • אירוע בשם abort משודר על אובייקט ה-signal
    • הפרופרטי signal.reason מתעדכן עם ערך שמתאר את סיבת הביטול (אם סופקה)
  • האזנה לאירועים

    פונקציות כמו fetch רושמות מאזין אירועים (event listener) לאירוע ה-abort של ה-signal, ומבטלות את הפעולה שלהן כאשר האירוע מתקבל.

// Simplified demonstration of the internal abort signaling mechanism in fetch
function internalFetch(url, { signal } = {}) {
  // Check if the external signal is already aborted
  if (signal?.aborted) {
    return Promise.reject(new DOMException('הבקשה בוטלה', 'AbortError'));
  }
  
   // Create a new internal AbortController for the actual fetch
  const controller = new AbortController();
  const internalSignal = controller.signal;
  
  // Listen for the abort event on the external signal
  if (signal) {
    signal.addEventListener('abort', () => {
       // Propagate the abort to the internal controller
      controller.abort(signal.reason);
    });
  }
  
  // Execute fetch with the internal signal
  return fetch(url, { signal: internalSignal });
}

שימושים נפוצים ל-AbortController

  • ביטול בקשות fetch

    השימוש הנפוץ ביותר הוא ביטול בקשות רשת שנעשות באמצעות הפונקציה fetch. זה שימושי במיוחד כאשר המשתמש עובר לעמוד אחר, או כאשר הנתונים כבר לא רלוונטיים.

  • הגדרת timeout לבקשות

    אפשר להשתמש ב-AbortController כדי להגדיר timeout לבקשות, כך שאם הבקשה לוקחת יותר מדי זמן, היא תבוטל אוטומטית.

    function fetchWithTimeout(url, options = {}, timeout = 5000) {
      const controller = new AbortController();
      const { signal } = controller;

      const timeoutId = setTimeout(() => {
        controller.()abort;
      }, timeout);

      return fetch(url, { ...options, signal })
        .finally(() => clearTimeout(timeoutId));
    }
  • ביטול מספר בקשות במקביל

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

    const controller = new AbortController();
    const { signal } = controller;

    // מספר בקשות שמשתמשות באותו signal
    const fetchUsers = fetch('/api/users', { signal });
    const fetchProducts = fetch('/api/products', { signal });

    // ביטול כל הבקשות בפעולה אחת
    controller.()abort;
  • ביטול האזנה לאירועים ב-DOM

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

    const controller = new AbortController();
    const { signal } = controller;

    // Add event listener with signal
    document.addEventListener('click', event => {
      console.log('Click event:', event);
    }, { signal });

    // Cancel the event listener
    controller.()abort;

התפתחויות חדשות

בגרסאות חדשות יותר של JavaScript, נוספו מספר תכונות ומתודות שהופכות את העבודה עם AbortController לנוחה אף יותר:

  • AbortSignal.timeout()

    מתודה סטטית חדשה שמאפשרת ליצור signal שיבוטל אוטומטית אחרי זמן מוגדר, ללא צורך ליצור controller נפרד.

    // יצירת signal שיבוטל אחרי 5 שניות
    const timeoutSignal = AbortSignal.timeout(5000);

    fetch('https://api.example.com/data', { signal: timeoutSignal });
  • סיבת ביטול (reason)

    בגרסאות חדשות יותר, אפשר להעביר סיבה לפעולת ה-abort, שתהיה זמינה דרך התכונה signal.reason:

    const controller = new AbortController();

    // ביטול עם סיבה
    controller.abort(new Error('המשתמש ביטל את הפעולה'));

    // גישה לסיבה
    console.log(controller.signal.reason);

סיכום

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

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

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

שאלות נפוצות על AbortController

מה זה AbortController ב-JavaScript?

AbortController הוא ממשק מובנה ב-JavaScript שמאפשר לבטל פעולות אסינכרוניות כמו בקשות fetch. הוא חלק מה-DOM API ותומך בכל הדפדפנים המודרניים. ה-AbortController מספק מתודה אחת — abort() — שמשדרת אות ביטול לכל פעולה שמאזינה לו.

מה ההבדל בין AbortController ל-AbortSignal?

AbortController הוא האובייקט השולט — הוא מספק את מתודת abort(). AbortSignal הוא האובייקט שמועבר לפונקציות כמו fetch — הוא מאזין לאות הביטול ומגיב בהתאם. הם עובדים יחד: אתה שולט דרך ה-controller, והפעולות האסינכרוניות מאזינות דרך ה-signal.

איך מבטלים בקשת fetch עם AbortController?

יוצרים instance של AbortController, מעבירים את controller.signal כפרמטר ל-fetch, ואז קוראים ל-controller.abort() כשרוצים לבטל. הבקשה תיזרק עם שגיאה מסוג AbortError שאפשר לתפוס ב-catch ולבדוק אם err.name === 'AbortError'.

האם AbortController עובד רק עם fetch?

לא. AbortController עובד גם עם addEventListener — ניתן להעביר signal כאפשרות שלישית ל-addEventListener, וביטול ה-controller יסיר את המאזין אוטומטית. בנוסף, ספריות רבות כמו axios מציעות תמיכה דרך מנגנון דומה.

מה זה AbortSignal.timeout() ומתי כדאי להשתמש בו?

AbortSignal.timeout() היא מתודה סטטית שמייצרת signal שמבטל את הבקשה אוטומטית לאחר מספר המילישניות שציינת. זהו קיצור נוח שמחליף את הצורך ליצור AbortController ו-setTimeout באופן ידני. כדאי להשתמש בה כשרוצים להגדיר timeout פשוט לבקשת fetch.