تعلم كيفية استخدام 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 في تحسين الجودة العامة لتطبيقات الويب الخاصة بك.