Series: JavaScript Basics Lesson 61

AbortController

AbortController nima ekani va fetch()ni bekor qilish uchun nega kerakligi, race condition va memory leak muammolarini qanday hal qilishi, EventTarget asosida qanday ishlashi, bitta signal bilan bir nechta operatsiyani bekor qilish, va AbortSignal.timeout().

61-dars

AbortController

AbortController — boshlangan asinxron operatsiyani (eng ko'p fetch() bilan ishlatiladi) tashqaridan, istalgan vaqtda bekor qilish imkonini beruvchi, brauzer taqdim qiladigan standart mexanizm.

const controller = new AbortController();

fetch('https://api.example.com/users', { signal: controller.signal })
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(err => {
    if (err.name === 'AbortError') {
      console.log("So'rov bekor qilindi");
    }
  });

// 2 sekunddan keyin, agar hali tugamagan bo'lsa — bekor qilamiz
setTimeout(() => controller.abort(), 2000);

Ikkita asosiy narsa bor:

  • controller.signal — bu, fetch (yoki boshqa qo'llab-quvvatlaydigan API)ga "meni kuzatib tur" deb beriladigan obyekt
  • controller.abort() — chaqirilganda, ushbu signalga bog'langan barcha operatsiyalarga "to'xta" signali yuboriladi

1. Nega kerak — bekor qilmasa nima yomon bo'ladi?

Muammo 1: Qidiruv so'rovlarida "eskisi keyin kelib qolishi"

searchInput.addEventListener('input', async (e) => {
  const response = await fetch(`/search?q=${e.target.value}`);
  const results = await response.json();
  renderResults(results); // muammo shu yerda!
});

Foydalanuvchi "j", "ja", "jav" deb tez-tez yozsa — uchta alohida fetch ketadi. Tarmoq tezligi tasodifiy bo'lgani uchun, "j" so'rovi oxirgi bo'lib javob qaytarishi mumkin (tarmoq kechikishi tasodifiy tartibda bo'ladi). Natijada — ekranda "jav" uchun emas, balki eskirgan "j" natijalari ko'rsatiladi, garchi foydalanuvchi allaqachon to'liq so'z yozgan bo'lsa ham.

Bu — race condition (Single-thread model mavzusida ko'rgan tushunchaning tarmoq darajasidagi ko'rinishi). Yechim: har yangi so'rovdan oldin, eskisini bekor qilish:

let currentController = null;

searchInput.addEventListener('input', async (e) => {
  if (currentController) currentController.abort(); // eskisini o'ldiramiz
  currentController = new AbortController();

  try {
    const response = await fetch(`/search?q=${e.target.value}`, {
      signal: currentController.signal
    });
    const results = await response.json();
    renderResults(results);
  } catch (err) {
    if (err.name !== 'AbortError') console.error(err);
  }
});

Muammo 2: Keraksiz resurs sarfi

Foydalanuvchi sahifadan boshqa joyga o'tib ketsa (masalan, React'da komponent unmount bo'lsa), lekin fetch() hali davom etayotgan bo'lsa — javob kelganda, endi hech kimga kerak bo'lmagan ma'lumotni qayta ishlash uchun CPU va xotira sarflanadi, va ba'zida "mavjud bo'lmagan elementni yangilashga urinish" kabi xatolarga olib keladi.


2. Brauzer darajasida — bu qanday ishlaydi

AbortController — Observer patternning brauzer tomonidan standartlashtirilgan varianti, aniqrog'i — EventTarget asosida qurilgan:

const controller = new AbortController();
console.log(controller.signal instanceof EventTarget); // true

AbortSignal — o'zi EventTargetdan meros oladi (xuddi DOM elementlari kabi, DOM nima mavzusida ko'rganimizdek), demak unda addEventListener ishlaydi:

controller.signal.addEventListener('abort', () => {
  console.log('Signal berildi!');
});

controller.abort() chaqirilganda, ichkarida quyidagi zanjir ishga tushadi:

  1. signal.aborted — falsedan **true**ga o'zgaradi (bu — signal obyektining ichki holati, Promise'dagi pending → rejected holatiga o'xshash, lekin bu — oddiy boolean flag)
  2. signal ustida abort hodisasi ishga tushadi (xuddi DOM'dagi click kabi — EventTargetning umumiy mexanizmi)
  3. fetch() — ichkarida, o'zi shu signalga abort listener qo'ygan bo'ladi (siz buni ko'rmaysiz, brauzer avtomatik qiladi, siz faqat signalni fetchga uzatasiz)
  4. Bu listener ishga tushgach, brauzerning tarmoq qatlami darhol shu so'rovga tegishli TCP ulanishni yopadi, resurslarni bo'shatadi
  5. fetch()ning Promise'i — reject bo'ladi, AbortError turidagi xato bilan (DOMException, name: 'AbortError')

Muhim: abort() chaqirilishi — sinxron amal (signal holati darhol o'zgaradi), lekin fetch Promise'ining reject bo'lishi — avvalda ko'rgan microtask sifatida navbatga qo'yiladi, xuddi oddiy Promise rad etilishi kabi.


3. Bitta signal — bir nechta operatsiyani birga bekor qilish

AbortController'ning kuchi shundaki, bitta signalni bir nechta fetch'ga (yoki boshqa qo'llab-quvvatlovchi API'ga) ulash mumkin:

const controller = new AbortController();

fetch('/api/users', { signal: controller.signal });
fetch('/api/posts', { signal: controller.signal });
fetch('/api/comments', { signal: controller.signal });

// Bittasini chaqirish — HAMMASINI bekor qiladi
controller.abort();

Bu — masalan, sahifa komponenti butunlay yopilganda, unga tegishli barcha so'rovlarni bir zumda to'xtatish uchun juda qulay.


4. AbortController — faqat fetch uchun emas

Bu — umumiy mexanizm, boshqa ko'plab zamonaviy Web API'lar ham signalni qabul qiladi:

// addEventListener — signal orqali listener'ni avtomatik olib tashlash
document.addEventListener('click', handler, { signal: controller.signal });
// controller.abort() chaqirilsa — bu listener AVTOMATIK removeEventListener qilinadi!

// setTimeout kabi timer'lar uchun to'g'ridan-to'g'ri yo'q, lekin signal orqali qo'lda bog'lash mumkin
controller.signal.addEventListener('abort', () => clearTimeout(timerId));

addEventListenerda signal ishlatish — Event delegation mavzusida ko'rgan xotira oqib ketishi (memory leak) muammosining yechimi sifatida ham foydali: ko'p listener qo'shilgan, keyin hammasini birma-bir removeEventListener bilan tozalash o'rniga, bitta abort() — hammasini tozalaydi.


5. Timeout bilan birga ishlatish — AbortSignal.timeout()

Zamonaviy brauzerlarda, qo'lda setTimeout yozmasdan, tayyor qisqartma bor:

fetch('https://api.example.com/data', {
  signal: AbortSignal.timeout(5000) // 5 sekunddan keyin avtomatik bekor qiladi
});

Bu — ichkarida xuddi yuqoridagi setTimeout(() => controller.abort(), 5000) bilan bir xil ishlaydi, faqat brauzer buni siz uchun tayyor holda taqdim etadi.


Xulosa

Tushuncha Vazifasi
AbortController Signal yaratuvchi va boshqaruvchi obyekt
controller.signal API'larga uzatiladigan, kuzatiladigan obyekt (EventTarget asosida)
controller.abort() Signalni faollashtiradi — barcha bog'langan operatsiyalarni to'xtatadi
AbortError Bekor qilingan operatsiya qaytaradigan maxsus xato turi

AbortController — EventTarget asosida qurilgan, bitta markazdan boshqariladigan "bekor qilish signali", u race condition (eski so'rov natijasi yangisini bosib ketishi), keraksiz tarmoq/resurs sarfi va memory leak muammolarini, bitta oddiy abort() chaqiruvi orqali hal qiladi.