تعلم كيفية استخدام JavaScript AbortController لإلغاء العمليات غير المتزامنة بفعالية مثل طلبات الاسترجاع والمؤقتات والمزيد.
JavaScript AbortController: إتقان إلغاء العمليات غير المتزامنة
في تطوير الويب الحديث، العمليات غير المتزامنة منتشرة في كل مكان. غالبًا ما تتضمن جلب البيانات من واجهات برمجة التطبيقات، وتعيين المؤقتات، والتعامل مع تفاعلات المستخدمين التعليمات البرمجية التي تعمل بشكل مستقل وربما لفترة طويلة. ومع ذلك، هناك سيناريوهات تحتاج فيها إلى إلغاء هذه العمليات قبل اكتمالها. هذا هو المكان الذي تأتي فيه واجهة AbortController
في JavaScript للإنقاذ. إنها توفر طريقة نظيفة وفعالة لإشارة طلبات الإلغاء إلى عمليات DOM والمهام غير المتزامنة الأخرى.
فهم الحاجة إلى الإلغاء
قبل الغوص في التفاصيل الفنية، دعنا نفهم سبب أهمية إلغاء العمليات غير المتزامنة. ضع في اعتبارك هذه السيناريوهات الشائعة:
- تنقل المستخدم: يبدأ المستخدم استعلام بحث، مما يؤدي إلى طلب API. إذا انتقلوا بسرعة إلى صفحة مختلفة قبل اكتمال الطلب، يصبح الطلب الأصلي غير ذي صلة ويجب إلغاؤه لتجنب حركة مرور الشبكة غير الضرورية والآثار الجانبية المحتملة.
- إدارة المهلة: يمكنك تعيين مهلة لعملية غير متزامنة. إذا اكتملت العملية قبل انتهاء المهلة، فيجب عليك إلغاء المهلة لمنع تنفيذ التعليمات البرمجية الزائدة عن الحاجة.
- إلغاء تحميل المكون: في أطر عمل الواجهة الأمامية مثل React أو Vue.js، غالبًا ما تقدم المكونات طلبات غير متزامنة. عندما يتم إلغاء تحميل مكون، يجب إلغاء أي طلبات جارية مرتبطة بهذا المكون لتجنب تسرب الذاكرة والأخطاء الناتجة عن تحديث المكونات التي تم إلغاء تحميلها.
- قيود الموارد: في البيئات المقيدة بالموارد (مثل الأجهزة المحمولة والأنظمة المضمنة)، يمكن أن يؤدي إلغاء العمليات غير الضرورية إلى تحرير موارد قيمة وتحسين الأداء. على سبيل المثال، إلغاء تنزيل صورة كبيرة إذا قام المستخدم بالتمرير خارج هذا القسم من الصفحة.
تقديم AbortController و AbortSignal
تم تصميم واجهة AbortController
لحل مشكلة إلغاء العمليات غير المتزامنة. وهي تتكون من مكونين رئيسيين:
- AbortController: يتحكم هذا الكائن في إشارة الإلغاء. لديه طريقة واحدة،
abort()
، والتي تُستخدم للإشارة إلى طلب إلغاء. - 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();
شرح:
- نقوم بإنشاء مثيل
AbortController
. - نحصل على
AbortSignal
المرتبط منcontroller
. - نقوم بتمرير
signal
إلى خياراتfetch
. - إذا احتجنا إلى إلغاء الطلب، فإننا نستدعي
controller.abort()
. - في كتلة
.catch()
، نتحقق مما إذا كان الخطأ هوAbortError
. إذا كان الأمر كذلك، فإننا نعلم أن الطلب قد تم إلغاؤه.
التعامل مع AbortError
عند استدعاء controller.abort()
، سيتم رفض طلب fetch
باستخدام AbortError
. من الضروري التعامل مع هذا الخطأ بشكل مناسب في التعليمات البرمجية الخاصة بك. قد يؤدي عدم القيام بذلك إلى رفض الوعود غير المعالجة والسلوك غير المتوقع.
إليك مثال أكثر قوة للتعامل مع الأخطاء:
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:
- تحقق من اسم الخطأ: تحقق دائمًا مما إذا كان
error.name === 'AbortError'
للتأكد من أنك تتعامل مع نوع الخطأ الصحيح. - إرجاع قيمة افتراضية أو إعادة رمي: اعتمادًا على منطق تطبيقك، قد ترغب في إرجاع قيمة افتراضية (مثل
null
) أو إعادة رمي الخطأ ليتم التعامل معه في أعلى سلسلة الاستدعاء. - تنظيف الموارد: إذا خصصت العملية غير المتزامنة أي موارد (مثل المؤقتات، مستمعي الأحداث)، فقم بتنظيفها في معالج
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();
شرح:
- تأخذ الدالة
cancellableTimeout
استدعاء، وتأخير، وAbortSignal
كوسيطات. - يُنشئ
setTimeout
ويخزن معرف المهلة. - يضيف مستمع حدث إلى
AbortSignal
الذي يستمع إلى حدثabort
. - عند تشغيل حدث
abort
، يزيل مستمع الحدث المهلة ويرفض الوعد.
إلغاء مستمعي الأحداث
على غرار المؤقتات، يمكنك استخدام 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();
شرح:
- نقوم بتمرير
signal
كخيار إلى طريقةaddEventListener
. - عندما يتم استدعاء
controller.abort()
، سيتمت إزالة مستمع الحدث تلقائيًا.
AbortController في مكونات React
في React، يمكنك استخدام AbortController
لإلغاء العمليات غير المتزامنة عندما يتم إلغاء تحميل مكون. هذا ضروري لمنع تسرب الذاكرة والأخطاء الناتجة عن تحديث المكونات التي تم إلغاء تحميلها. إليك مثال باستخدام الخطاف 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;
شرح:
- نقوم بإنشاء
AbortController
داخل الخطافuseEffect
. - نقوم بتمرير
signal
إلى طلبfetch
. - نقوم بإرجاع دالة تنظيف من الخطاف
useEffect
. سيتم استدعاء هذه الدالة عندما يتم إلغاء تحميل المكون. - داخل دالة التنظيف، نستدعي
controller.abort()
لإلغاء طلب الاسترجاع.
حالات الاستخدام المتقدمة
ربط إشارات الإلغاء
في بعض الأحيان، قد ترغب في ربط إشارات AbortSignal
المتعددة معًا. على سبيل المثال، قد يكون لديك مكون رئيسي يحتاج إلى إلغاء العمليات في مكوناته الفرعية. يمكنك تحقيق ذلك عن طريق إنشاء AbortController
جديد وتمرير إشارته إلى كل من المكونات الأصل والفرعية.
استخدام AbortController مع مكتبات الجهات الخارجية
إذا كنت تستخدم مكتبة تابعة لجهة خارجية لا تدعم AbortSignal
مباشرة، فقد تحتاج إلى تكييف التعليمات البرمجية الخاصة بك للعمل مع آلية الإلغاء الخاصة بالمكتبة. قد يتضمن ذلك تجميع وظائف المكتبة غير المتزامنة في وظائفك الخاصة التي تتعامل مع AbortSignal
.
فوائد استخدام AbortController
- تحسين الأداء: يمكن أن يؤدي إلغاء العمليات غير الضرورية إلى تقليل حركة مرور الشبكة واستخدام وحدة المعالجة المركزية واستهلاك الذاكرة، مما يؤدي إلى تحسين الأداء، خاصة على الأجهزة محدودة الموارد.
- كود أنظف: يوفر
AbortController
طريقة موحدة وأنيقة لإدارة الإلغاء، مما يجعل التعليمات البرمجية الخاصة بك أكثر قابلية للقراءة والصيانة. - منع تسرب الذاكرة: يؤدي إلغاء العمليات غير المتزامنة المرتبطة بالمكونات التي تم إلغاء تحميلها إلى منع تسرب الذاكرة والأخطاء الناتجة عن تحديث المكونات التي تم إلغاء تحميلها.
- تجربة مستخدم أفضل: يمكن أن يؤدي إلغاء الطلبات غير ذات الصلة إلى تحسين تجربة المستخدم من خلال منع عرض المعلومات القديمة وتقليل التأخير الملحوظ.
توافق المتصفح
يحظى AbortController
بدعم واسع النطاق في المتصفحات الحديثة، بما في ذلك Chrome و Firefox و Safari و Edge. يمكنك التحقق من جدول التوافق على MDN Web Docs للحصول على أحدث المعلومات.
ملء التعليمات البرمجية
بالنسبة للمتصفحات القديمة التي لا تدعم AbortController
بشكل أصلي، يمكنك استخدام ملء التعليمات البرمجية. ملء التعليمات البرمجية هو جزء من التعليمات البرمجية التي توفر وظائف ميزة أحدث في المتصفحات القديمة. هناك العديد من ملء التعليمات البرمجية AbortController
المتاحة عبر الإنترنت.
الخلاصة
واجهة AbortController
هي أداة قوية لإدارة العمليات غير المتزامنة في JavaScript. باستخدام AbortController
، يمكنك كتابة تعليمات برمجية أنظف وأكثر أداءً وأكثر قوة تتعامل مع الإلغاء بأناقة. سواء كنت تجلب بيانات من واجهات برمجة التطبيقات، أو تقوم بتعيين المؤقتات، أو تدير مستمعي الأحداث، يمكن أن يساعدك AbortController
في تحسين الجودة العامة لتطبيقات الويب الخاصة بك.