تخطَّ إلى المحتوى

المؤقتات في JavaScript: setTimeout و setInterval

الدرس 21 من 28 · JavaScript

رسالة «تم الحفظ» تظهر ثم تختفي بعد ثلاث ثوانٍ. ساعة رقمية تتحدث كل ثانية. عدّ تنازلي لنهاية العرض. شريط صور ينتقل تلقائياً. كل هذه تحتاج أن تقول للمتصفح: «نفّذ هذا لاحقاً» أو «نفّذه كل فترة». في هذا الدرس ستتعلم دالتين لذلك، وستفهم معهما أول أسرار JavaScript: كيف تتعامل مع الوقت.

console.log("بدأ البرنامج");
setTimeout(() => {
console.log("مرّت ثانية");
}, 1000);
الناتج
بدأ البرنامج
مرّت ثانية

setTimeout تأخذ معاملين:

  1. دالة تُنفَّذ لاحقاً.
  2. مدة الانتظار بالملّي ثانية. كل 1000 ملّي ثانية تساوي ثانية واحدة.

تخيّلها مثل منبّه الهاتف: تضبطه على وقت، وتكمل يومك، وعندما يحين الوقت يرنّ.

المفاجأة: الكود لا ينتظر

Section titled “المفاجأة: الكود لا ينتظر”

ما ناتج هذا الكود؟ توقّعه قبل أن تقرأ الإجابة:

console.log("1");
setTimeout(() => {
console.log("2");
}, 500);
console.log("3");
الناتج
1
3
2

الرقم 3 طُبع قبل 2! السبب أن setTimeout لا توقف البرنامج. هي تسجّل المنبّه فقط وتعود فوراً، فيكمل البرنامج إلى السطر التالي. وبعد نصف ثانية تُنفَّذ الدالة المؤجلة.

هذا السلوك يسمى غير متزامن (Asynchronous): الكود لا يُنفَّذ بترتيب كتابته. وهو أساس كل ما سيأتي: جلب البيانات من الإنترنت وانتظار المستخدم.

setTimeout(() => {
console.log("المؤجل");
}, 0);
console.log("العادي");
الناتج
العادي
المؤجل

حتى بمدة صفر تُنفَّذ الدالة المؤجلة بعد انتهاء الكود العادي كله. القاعدة: JavaScript تنهي ما بيدها أولاً، ثم تلتفت إلى ما في قائمة الانتظار.

تخيّل موظفاً واحداً على شباك: يخدم من أمامه حتى ينتهي، ثم ينادي التالي في الطابور. مهما كان التالي مستعجلاً، لن يقطع على من يُخدم الآن. JavaScript تعمل بنفس الطريقة: مهمة واحدة في كل لحظة.

setTimeout تُرجع رقماً يعرّف المؤقت. احفظه إذا أردت الإلغاء لاحقاً:

const timerId = setTimeout(() => {
console.log("لن أُطبع أبداً");
}, 1000);
clearTimeout(timerId);
console.log("أُلغي المؤقت");
الناتج
أُلغي المؤقت

استخدام شائع: رسالة تختفي بعد 5 ثوانٍ، لكن المستخدم أغلقها بيده قبل ذلك، فتلغي المؤقت حتى لا يحاول إخفاء رسالة غير موجودة.

setTimeout تنفذ مرة واحدة. أما setInterval فتكرر التنفيذ كل فترة إلى أن توقفها:

let count = 0;
const intervalId = setInterval(() => {
count++;
console.log("نبضة", count);
if (count === 3) {
clearInterval(intervalId);
console.log("توقف");
}
}, 200);
الناتج
نبضة 1
نبضة 2
نبضة 3
توقف
  • setInterval تُرجع رقماً تعريفياً مثل setTimeout.
  • clearInterval توقف التكرار. بدونها يستمر المؤقت إلى الأبد.
setTimeout setInterval
عدد مرات التنفيذ مرة واحدة يتكرر
الإيقاف clearTimeout clearInterval
أمثلة إخفاء رسالة، تأخير بحث ساعة، عدّ تنازلي، عرض شرائح

يمكنك إعطاء المؤقت اسم دالة موجودة بدلاً من دالة سهمية:

function sayHello() {
console.log("أهلاً!");
}
setTimeout(sayHello, 300);
الناتج
أهلاً!

نكتب sayHello بدون أقواس. لو كتبت setTimeout(sayHello(), 300) لنُفِّذت الدالة فوراً، وأُعطي المؤقت ناتجها بدلاً منها.

جرّب بنفسكJavaScript
أو اضغط Ctrl + Enter

الناتج

اضغط «ابدأ» وراقب الرقم ينقص كل ثانية حتى يصل إلى «انتهى الوقت!».

أهم سطر في المشروع هو أول سطر داخل زر «ابدأ»: if (intervalId !== null || seconds === 0) return;. هذا الشرط يمنع تشغيل مؤقت ثانٍ إذا ضغط المستخدم «ابدأ» مرتين. احذفه مؤقتاً واضغط الزر عدة مرات بسرعة، وشاهد العدّاد يجري بجنون.

ولاحظ نمط render من مشروع العدّاد: البيانات في متغير seconds، ودالة واحدة تعرضها.

  1. غيّر العدّ التنازلي ليبدأ من 30 ثانية.
  2. اجعل الرقم يتحول إلى اللون الأحمر عندما يبقى 3 ثوانٍ أو أقل.
  3. أضف رسالة تظهر عند انتهاء الوقت، ثم تختفي بعد ثانيتين باستخدام setTimeout.
  4. تحدٍّ: حوّل العدّاد إلى ساعة إيقاف تعدّ تصاعدياً، وتعرض الوقت بالشكل 01:25.
  • كتابة الدالة بأقواس: setTimeout(save(), 1000) تنفذ save فوراً. الصحيح setTimeout(save, 1000).
  • نسيان clearInterval: المؤقت يعمل بلا نهاية. احفظ الرقم التعريفي وأوقفه عند انتهاء الحاجة.
  • توقع أن الكود ينتظر: السطر الذي يلي setTimeout يُنفَّذ فوراً. ما تريد تأجيله ضعه داخل الدالة المؤجلة.

سؤال

كم مرة تُنفَّذ الدالة المعطاة لـ setTimeout؟

سؤال

بأي ترتيب تُطبع القيم: console.log('أ') ثم setTimeout يطبع 'ب' بمدة 0 ثم console.log('ج')؟

  • setTimeout تنفذ دالة مرة واحدة بعد مدة بالملّي ثانية.
  • setInterval تكرر التنفيذ كل فترة حتى تستدعي clearInterval.
  • المؤقتات لا توقف البرنامج: الكود التالي يُنفَّذ فوراً، والمؤجل ينتظر دوره.
  • احفظ الرقم التعريفي لتستطيع الإلغاء بـ clearTimeout أو clearInterval.
  • لا تبدأ مؤقتاً جديداً قبل التأكد من إيقاف القديم.