📚 مراجعة سريعة: هذا التطبيق العملي مبني على مفهوم برمجي أساسي. راجع الدرس النظري من هنا أولاً.
تطبيق واستخدام دالة Debounce في JavaScript
بعد أن تعرفنا على المفهوم النظري لدالة Debounce وأهميتها، حان الوقت لنرى كيف يمكننا بناءها واستخدامها عمليًا في JavaScript. سنقوم بتحليل الكود المقدم خطوة بخطوة لفهم آلية عملها.
الكود البرمجي لدالة Debounce:
// دالة Debounce لتحسين الأداء وتقليل الضغط على الخوادم
function debounce(func, delay) {
let timeoutId; // متغير لتخزين معرف المؤقت
return function (...args) { // ترجع دالة جديدة (Wrapper Function)
clearTimeout(timeoutId); // 1. إلغاء أي مؤقت سابق موجود
timeoutId = setTimeout(() => { // 2. بدء مؤقت جديد
func.apply(this, args); // 3. تنفيذ الدالة الأصلية بعد انتهاء التأخير
}, delay);
};
}شرح الكود خطوة بخطوة:
-
function debounce(func, delay) { ... }:هذه هي الدالة الرئيسية التي تستقبل دالتين كمدخلات:
func: الدالة الأصلية التي نريد تأخير تنفيذها (مثل دالة إرسال طلب البحث).delay: المدة الزمنية بالمللي ثانية التي يجب انتظارها قبل تنفيذfuncبعد آخر استدعاء.
-
let timeoutId;:هذا المتغير يتم تعريفه خارج الدالة الداخلية ولكنه متاح لها (بسبب مفهوم Closures في JavaScript). وظيفته هي تخزين المعرف الفريد للمؤقت الذي يتم إنشاؤه بواسطة
setTimeout. هذا المعرف ضروري لإلغاء المؤقت لاحقًا باستخدامclearTimeout. -
return function (...args) { ... };:دالة
debounceلا تنفذfuncمباشرة، بل ترجع دالة جديدة. هذه الدالة الجديدة هي التي سيتم استدعاؤها في كل مرة يحدث فيها الحدث (مثل ضغط المفتاح). تستخدم...argsلالتقاط جميع الوسائط التي تم تمريرها إلى الدالة الداخلية، بحيث يمكن تمريرها لاحقًا إلى الدالة الأصليةfunc. -
clearTimeout(timeoutId);:هذه هي الخطوة المحورية في دالة Debounce. في كل مرة يتم فيها استدعاء الدالة الداخلية (التي تم إرجاعها)، فإن أول شيء تفعله هو إلغاء أي مؤقت سابق كان قد بدأ. هذا يضمن أننا نعيد "تعيين" عداد التأخير في كل مرة يحدث فيها الحدث.
-
timeoutId = setTimeout(() => { ... }, delay);:بعد إلغاء المؤقت السابق (إذا وجد)، يتم إنشاء مؤقت جديد. هذا المؤقت سيقوم بتنفيذ الدالة التي بداخله بعد مرور فترة
delay. -
func.apply(this, args);:هذا هو المكان الذي يتم فيه تنفيذ الدالة الأصلية
func. يتم استخدامapplyلضمان أن يتم استدعاءfuncفي سياق (this) الدالة الداخلية، ولتمرير جميع الوسائط (args) التي تم استلامها.
مثال عملي: محاكاة مربع بحث
لنرى كيف يعمل هذا عمليًا باستخدام المثال المقدم:
// تجربة الدالة (محاكاة مربع بحث)
const handleSearch = debounce((query) => {
console.log(`جاري جلب بيانات البحث عن: "${query}" من الخادم...`);
}, 500); // تأخير نصف ثانية
// محاكاة مستخدم يكتب كلمة "جوارا" بسرعة شديدة
handleSearch('ج'); // تبدأ مؤقت (1)
handleSearch('جو'); // تلغي مؤقت (1)، تبدأ مؤقت (2)
handleSearch('جوارا'); // تلغي مؤقت (2)، تبدأ مؤقت (3)
// بعد نصف ثانية من آخر استدعاء (أي بعد handleSearch('جوارا'))، سيتم تنفيذ الدالة
// وسيكون الناتج في الـ console:
// جاري جلب بيانات البحث عن: "جوارا" من الخادم...
في هذا المثال، قمنا بإنشاء نسخة "مؤجلة" من دالة البحث handleSearch التي ستنتظر 500 مللي ثانية (نصف ثانية) بعد آخر ضغطة مفتاح قبل أن تنفذ عملية البحث الفعلية. عندما تتم محاكاة كتابة "ج" ثم "جو" ثم "جوارا" بسرعة، فإن الاستدعاءات الأولى والثانية يتم إلغاء مؤقتاتها بواسطة الاستدعاءات اللاحقة. فقط الاستدعاء الأخير (handleSearch('جوارا')) هو الذي يكمل مؤقته ويتم تنفيذ الدالة الأصلية معه.
كيفية دمجها في مشروعك:
يمكنك استخدام دالة Debounce مع أي حدث يتكرر بسرعة. على سبيل المثال، في حقل إدخال HTML:
<input type="text" id="search-input" placeholder="اكتب للبحث...">const searchInput = document.getElementById('search-input');
// الدالة الأصلية التي تقوم بالبحث
const performSearch = (query) => {
console.log(`البحث عن: ${query}`);
// هنا يمكنك إرسال طلب AJAX إلى الخادم
};
// إنشاء نسخة مؤجلة من دالة البحث
const debouncedSearch = debounce(performSearch, 300); // تأخير 300 مللي ثانية
// ربط الدالة المؤجلة بحدث 'keyup'
searchInput.addEventListener('keyup', (event) => {
debouncedSearch(event.target.value);
});بهذه الطريقة، لن يتم تشغيل performSearch إلا بعد 300 مللي ثانية من توقف المستخدم عن الكتابة، مما يحسن الأداء بشكل كبير.