لو المستخدم فتح صفحة فيها 5 طلبات fetch وضغط رجوع قبل ما ترجع، الطلبات بتفضل شغالة، والمتصفح بيحاول يحدّث مكونات اتشالت من الشاشة. النتيجة: تسريب ذاكرة وwarnings في الكونسول. AbortController هو الحل الرسمي من المتصفح، والمقال ده بيديك الكود بالظبط، وأرقام حقيقية قبل وبعد، وحالات ما ينفعش تستخدمه فيها.
المشكلة باختصار
الـ fetch API ملهاش زرار cancel. لما بتبعت طلب، بيفضل شغال لحد ما السيرفر يرد أو يحصل timeout. في تطبيقات React أو Vue، ده بيخلق سيناريو متكرر:
- المكوّن يعمل mount ويبدأ fetch.
- المستخدم يغيّر الصفحة قبل ما الطلب يرجع.
- الطلب يرجع بعد ما المكوّن اتشال.
- الكود بيحاول يعمل
setStateعلى مكوّن مش موجود.
الكونسول بيطلع warning اسمه "Can't perform a React state update on an unmounted component"، ومع كل navigation المشكلة بتكبر.
AbortController باختصار
AbortController هو Web API موجود في كل المتصفحات الحديثة وNode.js 15+. بيديك كائنين:
controller.signal: إشارة بتتربط بالطلب.controller.abort(): دالة بتوقف الطلب فورًا وترميAbortError.
بالظبط زي الريموت بتاع التلفزيون بيقفله من بعيد، الـ signal بيتربط بالـ fetch ويقدر يوقفه من غير ما تلمس الطلب نفسه.
الكود بالظبط
مثال عملي في React — يدخل في useEffect ويطلع في cleanup:
import { useEffect, useState } from 'react';
function UsersList() {
const [users, setUsers] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
const controller = new AbortController();
fetch('/api/users', { signal: controller.signal })
.then((res) => res.json())
.then((data) => setUsers(data))
.catch((err) => {
if (err.name === 'AbortError') return;
setError(err);
});
return () => controller.abort();
}, []);
return error ? <p>{error.message}</p> : <ul>{/* ... */}</ul>;
}السطرين المهمين: new AbortController() في البداية، وcontroller.abort() في الـ return بتاع useEffect. الباقي نفس الـ fetch العادي.
أرقام حقيقية من قياس على تطبيق React
اختبار على تطبيق فيه 6 صفحات بـ fetch متعدد، قبل إضافة AbortController وبعدها، على Chrome DevTools Memory profiler:
- قبل: بعد 50 navigation متتالي، الذاكرة ارتفعت من 48MB لـ 140MB.