Menu

EventEmitter في Node.js: on وemit وonce والأحداث المخصصة

يتيح EventEmitter من وحدة events في Node لكائن ما أن يعلن أحداثًا مسماة عبر emit، ويتيح لشيفرة أخرى أن تستجيب عبر on أو once. يعمل المستمعون بشكل متزامن، بترتيب إضافتهم.

تحتوي هذه الصفحة على محررات قابلة للتشغيل. حرّر، شغّل، وشاهد النتيجة فوراً.

EventEmitter هو الصنف في وحدة events في Node الذي يتيح لكائن أن يعلن أحداثًا مسماة. تسجّل مستمعًا بـ emitter.on('name', fn) وتطلقه بـ emitter.emit('name', ...args)؛ فيعمل كل مستمع لذلك الاسم، بترتيب إضافته، مع الوسائط التي مرّرتها. خوادم HTTP والتدفقات وprocess كلها EventEmitter، فتعمل هذه الدوال القليلة نفسها في كل مكان في Node.

يعمل المستمعان كلاهما بين before emit وafter emit: تستدعيهما emit بشكل متزامن ولا تعود إلا حين ينتهيان. بدّل موضعي استدعاءي shop.on فيتبدّل السطران الأوسطان أيضًا.

on وonce وoff

تُبقي on المستمع حتى تزيله، وتزيله once بعد أول استدعاء، وتزيل off (وهي نفسها removeListener) مستمعًا واحدًا. لإزالة مستمع تحتاج إلى الدالة نفسها التي أضفتها، فسمِّها بدلًا من تمرير دالة سهمية مباشرة.

يُطبع first open ever مرة واحدة، بعد Ada. وبعد إزالة logOpen لا يبقى أحد يستمع، فتعيد emit الأخيرة false؛ وكان door.removeAllListeners('open') سيزيل المستمعين كليهما في استدعاء واحد.

الدالةماذا تفعل
on(name, fn) وaddListenerتستدعي fn مع كل emit
once(name, fn)تستدعي fn مع emit التالية فقط
prependListener(name, fn)مثل on، لكنها تعمل قبل المستمعين الموجودين
off(name, fn) وremoveListenerتزيل تلك الدالة
emit(name, ...args)تستدعي المستمعين، وتعيد true إن وُجد أي منهم
listenerCount(name)عدد مستمعي الحدث

حدث error

للحدث 'error' وضع خاص. إذا أطلقته ولم يكن أحد يستمع، ترمي emit الخطأ، وفي برنامج حقيقي يؤدي الخطأ غير الملتقط إلى انهيار العملية. أضف دائمًا مستمعًا لـ 'error' إلى كل EventEmitter قد يفشل، مثل التدفقات والخوادم.

دون مستمع، ترمي emit الخطأ فيلتقطه catch؛ وبعد إضافة المستمع، تستدعيه emit نفسها ببساطة ويتابع البرنامج. أما أي اسم حدث آخر بلا مستمعين فيُتجاهل بصمت.

وراثة EventEmitter في صنفك الخاص

الطريقة المعتادة لاستخدامه هي أن يرث صنفك EventEmitter، وتستدعي super() في الباني وthis.emit حين يحدث شيء ما. عندها يشترك المستدعون في كائنك كما في أي كائن من Node.

يطبع 25% و50% و75% و100% ثم photo.png uploaded. انقل up.start() فوق سطري up.on فلا يُطبع شيء: تنطلق الأحداث قبل أن يستمع أحد.

ينطبق الفخ نفسه على الإطلاق من داخل الباني. المكتبات التي يجب أن تطلق حدثًا فورًا تغلّف الاستدعاء بـ process.nextTick، التي تعمل بعد أسطر on لدى المستدعي (راجع صفحة حلقة الأحداث).

انتظار حدث عبر events.once

تعيد events.once(emitter, name) وعدًا يتحقق بوسائط الحدث، كمصفوفة، في المرة التالية التي يُطلق فيها. فتحوّل حدثًا لمرة واحدة إلى شيء يمكنك انتظاره بـ await، وترفض الوعد إن أطلق الكائن 'error' أولًا.

يتوقف await حتى يطلق المؤقت ready، ثم تُفكك المصفوفة إلى الوسيطين. وللحدث الذي ينطلق مرات كثيرة، تعطيك events.on(emitter, name) مكرّرًا غير متزامن (async iterator) يمكنك المرور عليه بـ for await.

أخطاء شائعة

إضافة مستمع داخل دالة تعمل كثيرًا. استدعاء on في كل معالج طلب يراكم المستمعين ويستهلك الذاكرة. أضفه مرة واحدة، أو استخدم once.

إزالة دالة مكتوبة مباشرة. off('data', () => ...) لا تزيل شيئًا، لأن تلك الدالة السهمية دالة جديدة. احتفظ بمرجع إلى الدالة التي أضفتها.

توقّع أن يعمل المستمعون لاحقًا. emit متزامنة، فالمستمع البطيء يؤخر الشيفرة التي أطلقت الحدث. ولا يفيد تعليم المستمع بـ async، لأن شيفرته تبقى تعمل على الخيط نفسه حتى أول await. قسّم العمل الثقيل إلى أجزاء بينها setImmediate، أو انقله إلى خيط عامل (worker thread).

الأسئلة الشائعة

هل EventEmitter غير متزامن؟

لا. تستدعي emit كل مستمع فورًا، واحدًا تلو الآخر، وتعود حين ينتهي الأخير. يمكن للشيفرة داخل المستمع أن تبدأ عملًا غير متزامن، لكن المستمع نفسه يُستدعى بشكل متزامن.

ماذا يعني التحذير MaxListenersExceededWarning؟

يحذّر Node حين يُضاف أكثر من 10 مستمعين لحدث واحد، لأن ذلك يعني غالبًا أن مستمعًا يُضاف مع كل طلب ولا يُزال أبدًا. أزل المستمعين الزائدين، أو استدعِ emitter.setMaxListeners(n) إن كنت تحتاج فعلًا إلى المزيد.

ماذا تعيد emit؟

true إن كان للحدث مستمع واحد على الأقل، وfalse إن لم يكن أحد يستمع. ولا تعيد ما يعيده المستمعون.

ما كائنات Node.js التي هي EventEmitter؟

معظم الكائنات الأساسية في Node: خوادم HTTP، والطلبات والاستجابات، والتدفقات (streams)، والمقابس، وprocess، وواجهات readline. لهذا تشترك كلها في on وonce وoff.

رسم توضيحي للغات البرمجة في Coddy

تعلّم البرمجة مع Coddy

ابدأ الآن