الفرق بين المراجعتين لصفحة: «Node.js/events»
لا ملخص تعديل |
ط استبدال النص - '\[\[تصنيف:(.*)\]\]' ب'{{SUBPAGENAME}}' |
||
(1 مراجعات متوسطة بواسطة نفس المستخدم غير معروضة) | |||
سطر 384: | سطر 384: | ||
==مصادر== | ==مصادر== | ||
*[https://nodejs.org/dist/latest-v10.x/docs/api/events.html صفحة Events في توثيق Node.js الرسمي]. | *[https://nodejs.org/dist/latest-v10.x/docs/api/events.html صفحة Events في توثيق Node.js الرسمي]. | ||
[[تصنيف:Node.js|{{SUBPAGENAME}}]] |
المراجعة الحالية بتاريخ 11:16، 23 أكتوبر 2018
الثبات: مستقر
الكثير من أساس الواجهة البرمجية Node.js مبني حول بنية توجهها أحداث متميزة غير متزامنة حيث تطلق أنواع معينة من الكائنات (تُسمى "مطلقات" [emitters]) أحداث معينة تستدعي كائنات Function ("منصتات" [listeners]).
على سبيل المثال: يطلق كائن net.server
حدثًا كلما اتصل نظير بالخادم مثال آخر لكائن fs.ReadStream
يطلق حدثًا عندما يُفتح ملف ما؛ ويطلق كائن stream
حدثًا كلما أتيحت بيانات للقراءة.
كل الكائنات التي تطلق أحداثًا هي من صنف مطلق الحدث EventEmitter
. حيث تكشف عن الدالة eventEmitter.on()
التي تتيح إلحاق دالة أو أكثر مع الأحداث المعينة التي يطلقها هذا الكائن. عادة ما تكون أسماء الأحداث ذات أحرف كبيرة في بدايتها (مثل ReadStream
) لكن يمكن استخدام أي معرف صالح في JavaScript.
عندما يطلق كائن مطلق الحدث EventEmitter
حدثًا، تُستدعى كل الدوال الملحقة بذلك الحدث تزامنيًا (synchronously). مع تجاهل أي قيم تعود من دوال المنصتات المستدعاة.
المثال التالي يعرض حالة مطلق حدث EventEmitter
بسيط مع منصت واحد. حيث نستخدم التابع eventEmitter.on()
لتسجيل المنصتات، والتابع eventEmitter.emit()
لإطلاق الحدث.
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
console.log('an event occurred!');
});
myEmitter.emit('event');
تمرير الوسائط وقيمة this
إلى المنصتات
يتيح التابع eventEmitter.emit()
مجموعة اختيارية من الوسائط لتمريرها إلى دوال المنصتات. لاحظ عند استدعاء دالة منصتة، ستشير قيمة المعامل this
إلى نسخة الكائن EventEmitter
الذي ارتبط به المنصت.
const myEmitter = new MyEmitter();
myEmitter.on('event', function(a, b) {
console.log(a, b, this, this === myEmitter);
// يطبع
// a b MyEmitter {
// domain: null,
// _events: { event: [Function] },
// _eventsCount: 1,
// _maxListeners: undefined } true
});
myEmitter.emit('event', 'a', 'b');
يمكن استخدام الدوال السهمية كمنصتات، لكن في هذه الحالة، لا تعود قيمة this
تدل على كائن مطلق الحدث EventEmitter
:
const myEmitter = new MyEmitter();
myEmitter.on('event', (a, b) => {
console.log(a, b, this);
// يطبع: a b {}
});
myEmitter.emit('event', 'a', 'b');
الاستدعاء المتزامن والاستدعاء غير المتزامن
يستدعي مطلق الحدث EventEmitter
جميع المنصتات تزامنيًا حسب ترتيب تسجيلها. من المهم الانتباه إلى الترتيب المناسب للأحداث لتجنب الظروف النادرة أو الأخطاء المنطقية. يمكن تحويل دوال الإنصات إلى الوضع غير المتزامن عند الضرورة باستخدام التوابع setImmediate()
أو process.nextTick()
:
const myEmitter = new MyEmitter();
myEmitter.on('event', (a, b) => {
setImmediate(() => {
console.log('هذا الحدث غير متزامن');
});
});
myEmitter.emit('event', 'a', 'b');
تنفيذ الأحداث مرة واحدة فقط
عند تسجيل منصت بالتابع eventEmitter.on()
، يُستدعى المنصت في كل مرة يُطلق فيها الحدث المعني.
const myEmitter = new MyEmitter();
let m = 0;
myEmitter.on('event', () => {
console.log(++m);
});
myEmitter.emit('event');
// يطبع: 1
myEmitter.emit('event');
// يطبع: 2
لكن يمكنك تنفيذ استدعاء المنصت مرة واحدة على الأكثر عند إطلاق حدث معين باستخدام التابع eventEmitter.once()
. بالتالي عندما يُطلق الحدث، يُزال تسجيل المنصت ثم يُستدعى.
const myEmitter = new MyEmitter();
let m = 0;
myEmitter.once('event', () => {
console.log(++m);
});
myEmitter.emit('event');
// Prints: 1
myEmitter.emit('event');
// Ignored
أحداث الأخطاء
عادة ما يُطلق الحدث "error
" "خطأ" عند حدوث خطأ ما في كائن مطلق الحدث EventEmitter
. وهذه حالات خاصة في Node.js.
إذا لم يكن لدى مطلق الحدث EventEmitter
منصتٌ واحدٌ مسجلًا على الأقل للحدث "error
"، وأُطلق الحدث "error
". فستطبع عملية Node.js
تتبُّعًا تكديسيًا ثم تخرج العملية (exit).
const myEmitter = new MyEmitter();
myEmitter.emit('error', new Error('whoops!'));
للحماية من انهيار عمل Node.js يمكن استخدام الوحدة domain
(لكن لا يُنصح بهذا إذ إنَّ الوحدة domain
قد أُهملت).
أفضل ممارسة في هذه الحالة، هي إضافة منصتات إلى أحداث "error" دائمًا.
const myEmitter = new MyEmitter();
myEmitter.on('error', (err) => {
console.error('whoops! there was an error');
});
myEmitter.emit('error', new Error('whoops!'));
// يطبع: whoops! there was an error
الصنف: EventEmitter
أُضيف في الإصدار 0.1.26.
تحدد وتكشف وحدة events
الصنف EventEmitter
:
const EventEmitter = require('events');
إذ تُطلِق جميع مطلقات الأحداث EventEmitter
الحدث "newListener
" مع إضافة منصت جديد والحدث "removeListener
" عند إزالة منصت موجود.
الحدث: 'newListener
'
أُضيف في الإصدار 0.1.26
eventName
<string> | <symbol>: اسم الحدث المُنصَت إليه.- listener <Function>: دالة الحدث.
يطلق الصنف EventEmitter
الحدث 'newListener
' الخاص به قبل إضافة منصت جديد إلى قائمة المنصتات الداخلية التابعة له.
تُمرر المنصتات المسجلة لحدث 'newListener
' اسم الحدث وإشارة إلى المنصت المضاف.
تشكل حقيقة تشغيل الحدث قبل إضافة المنصت أثرًا جانبيًا خفيًا ومهمًا: ستضاف أية منصتات إضافية مسجلة لنفس الاسم داخل استدعاء 'newListener
' قبل إضافة المنصت المعني.
const myEmitter = new MyEmitter();
// قم بهذا مرة واحدة فقط حتى لا ندخل في حلقة لا متناهية
myEmitter.once('newListener', (event, listener) => {
if (event === 'event') {
// أضف منصتًا جديدًا في المقدمة
myEmitter.on('event', () => {
console.log('B');
});
}
});
myEmitter.on('event', () => {
console.log('A');
});
myEmitter.emit('event');
// يطبع:
// B
// A
الحدث: 'removeListener
'
سجل التغييرات
الإصدار | التغييرات |
---|---|
6.1.0,
4.7.0 |
ينتج الوسيط listener الآن دالة المنصت الأصلية، بالنسبة للمنصات المرفقة باستخدام الدالة .once(). |
0.9.3 | أضيفت في الإصدار 0.9.3. |
eventName
<string> | <symbol>: اسم الحدث المُنصَت إليه.listener
<Function>: دالة الحدث.
يُطلق حدث 'removeListener
' بعد إزالة المنصت.
EventEmitter.listenerCount(emitter, eventName)
أضيف هذا التابع في الإصدار 0.9.12 وأهمل في الإصدار 4.0.0. درجة الاستقرار 0 - مهملة ويُنصَح باستخدام التابع emitter.listenerCount()
بدلًا منه.
تابع يعيد عدد المنصتات لحدث معين eventName
المسجلة على مُطلِق emitter
المعطي.
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {});
myEmitter.on('event', () => {});
console.log(EventEmitter.listenerCount(myEmitter, 'event'));
// تطبع: 2
EventEmitter.defaultMaxListeners
أضيفت في الإصدار: 0.11.2.
افتراضيًا، يمكن تسجيل حد أقصى 10 منصتات في الحدث الفردي. لكن يمكن تغيير هذا الحد لكائنات مطلق الحدث EventEmitter
باستخدام التابع emitter.setMaxListeners(n)
. لتغيير الحد الافتراضي لجميع كائنات EventEmitter
، يمكنك استخدام خاصية EventEmitter.defaultMaxListeners
. يجب أن تكون القيمة عددًا موجبًا، وإلا يطلق البرنامج الخطأ TypeError
.
انتبه عند تحديد EventEmitter.defaultMaxListeners
لأن هذا التغيير يؤثر على جميع كائنات مطلق الحدث EventEmitter
، بما فيها الموجودة بالفعل قبل التغيير. على كل حال، لا يزال لاستدعاء التابع emitter.setMaxListeners(n)
أولويةٌ على EventEmitter.defaultMaxListeners
.
لاحظ أن هذا ليس حدًا حاسمًا. حيث يتيح كائن مطلق الحدث EventEmitter
إضافة المزيد من المنصتات لكنه سيطلق تحذيرًا مفاده أن هناك "تسريب محتمل في ذاكرة EventEmitter
". يمكن استخدام التوابع emitter.getMaxListeners()
و emitter.setMaxListeners()
لأي نسخة من EventEmitter
لتجنب هذا التحذير مؤقتًا:
emitter.setMaxListeners(emitter.getMaxListeners() + 1);
emitter.once('event', () => {
// نص برمجي
emitter.setMaxListeners(Math.max(emitter.getMaxListeners() - 1, 0));
});
يمكنك استخدام خيار سطر الأوامر --trace-warnings
لعرض التتبع التكديسي لمثل هذه التحذيرات.
أيضًا يمكنك التحقق من التحذير المطلق من خلال process.on('warning')
مع خيارات emitter
، و type
و count
، التي تشير إلى كائن مطلق الحدث EventEmitter
، واسم الحدث وعدد المنصتات المرفقة، بالترتيب. اسم خاصية name
هو "MaxListenersExceededWarning
" لحدث التحذير.
emitter.addListener(eventName, listener)
أضيف في الإصدار: 0.1.26.
eventName
<string> | <symbol>: اسم الحدث المُنصَت إليه.listener
<Function>: دالة الحدث.
اسم بديل للتابع emitter.on(eventName, listener)
.
emitter.emit(eventName[, ...args])
أضيف في الإصدار: 0.1.26.
eventName
<string> | <symbol>: اسم الحدث المُنصَت إليه.-
...args
: أي نوع من البيانات. - القيمة المعادة: <boolean>.
تستدعي بشكل متزامن كل من المنصتات المسجلة للحدث المعني eventName
، بترتيب تسجيلها، مع تمرير الوسائط الموفّرة لكل منها.
يعيد هذا التابع قيمةً صحيحةً true
إذا كانت هناك منصتات مرتبطة بالحدث، أو القيمة false
إذا لم تكن كذلك.
emitter.eventNames()
أضيف في الإصدار: 6.0.0.
- القيمة المعادة: <Array>.
يعيد التابع مصفوفةً فيها قائمة الأحداث المُسجّلة. يمكن أن تكون القيم في المصفوفة نصوصًا أو رموزًا (من الكائن Symbol
).
const EventEmitter = require('events');
const myEE = new EventEmitter();
myEE.on('foo', () => {});
myEE.on('bar', () => {});
const sym = Symbol('symbol');
myEE.on(sym, () => {});
console.log(myEE.eventNames());
// [ 'foo', 'bar', Symbol(symbol) ]
emitter.getMaxListeners()
أضيف في الإصدار: 1.0.0.
- القيمة المعادة: <integer>.
إعادة قيمة الحد الأقصى لعدد المنصتات لكائن EventEmitter
الذي يمكن تحديده إما باستخدام التابعemitter.setMaxListeners(n)
أو أخذ القيمة الافتراضية من EventEmitter.defaultMaxListeners
.
emitter.listenerCount(eventName)
أضيف في الإصدار 3.2.0.
إعادة عدد المنصتات التي تستمع إلى وقوع الحدث المسمى eventName
.
emitter.listeners(eventName)
سجل التغييرات
الإصدار | التغييرات |
---|---|
7.0.0 | بالنسبة للمنصتات المرفقة بواسطة .once() تعيد هذه الطريقة المنصتات الأصلية بدلًا من الدوال المُغلِّفة الآن. |
0.1.26 | أضيفت في الإصدار 0.1.26. |
eventName
<string> | <symbol>: اسم الحدث المُنصَت إليه.- القيمة المعادة: <Function[]>.
إعادة نسخة من مصفوفة المنصتات للحدث المعني eventName.
server.on('connection', (stream) => {
console.log('someone connected!');
});
console.log(util.inspect(server.listeners('connection')));
// تطبع: [ [Function] ]
emitter.off(eventName, listener)
أضيف في الإصدار 10.0.0.
eventName
<string> | <symbol>: اسم الحدث المُنصَت إليه.listener
<Function>: دالة رد النداء (الاستدعاء).- القيمة المعادة:
<EventEmitter>
.
اسم بديل للتابع emitter.removeListener()
.
emitter.on(eventName, listener)
أضيف في الإصدار 0.1.101.
eventName
<string> | <symbol>: اسم الحدث المُنصَت إليه.listener
<Function>: دالة رد النداء (الاستدعاء).- القيمة المعادة:
<EventEmitter>
.
تضيف دالة المنصت listener
إلى نهاية مصفوفة المنصتات للحدث المعني eventName
. لاحظ أن الطريقة لا تتفحص ما إذا كان المنصت قد أُضيف بالفعل أم لا. بالتالي عند الاستدعاء المتكرر بنفس مجموعة الوسائط eventName
و listener
، يُضاف ويستدعى المنصت في كل مرة.
server.on('connection', (stream) => {
console.log('someone connected!');
});
تعيد إشارةً إلى كائن EventEmitter
، بحيث يمكن ربط الاستدعاءات كسلسلة (chain).
افتراضيًا، تُستدعى منصتات الحدث بنفس ترتيب تسجيلها. ولكن يمكنك استخدام التابع emitter.prependListener()
لإضافة منصت الحدث إلى بداية مصفوفة المنصتات.
const myEE = new EventEmitter();
myEE.on('foo', () => console.log('a'));
myEE.prependListener('foo', () => console.log('b'));
myEE.emit('foo');
// تطبع:
// b
// a
emitter.once(eventName, listener)
أضيف في الإصدار: 0.3.0.
eventName
<string> | <symbol>: اسم الحدث المُنصَت إليه.listener
<Function>: دالة رد النداء (callback).- القيمة المعادة:
<EventEmitter>
.
تضيف دالة listener
منصتًا لمرة واحدة إلى الحدث المعني eventName
. بحيث يُلغى المنصت عند وقوع الحدث eventName
ثم يُستدعى.
server.once('connection', (stream) => {
console.log('Ah, we have our first user!');
});
تعود بإشارة إلى كائن EventEmitter
، بحيث يمكن ربط الاستدعاءات كسلسلة (chain).
افتراضيًا، تُستدعى منصتات الحدث بنفس ترتيب تسجيلها. ولكن يمكنك استخدام التابع emitter.prependOnceListener()
لإضافة منصت الحدث إلى بداية مصفوفة المنصتات.
const myEE = new EventEmitter();
myEE.once('foo', () => console.log('a'));
myEE.prependOnceListener('foo', () => console.log('b'));
myEE.emit('foo');
// تطبع:
// b
// a
emitter.prependListener(eventName, listener)
أضيف في الإصدار: 6.0.0.
eventName
<string> | <symbol>: اسم الحدث المُنصَت إليه.listener
<Function>: دالة رد النداء (الاستدعاء).- القيمة المعادة:
<EventEmitter>
.
تضيف دالة المنصت listener
إلى بداية مصفوفة المنصتات للحدث المعني eventName
. لاحظ أن الطريقة لا تتفحص ما إذا كان المنصت قد أُضيف بالفعل أم لا. بالتالي عند الاستدعاء المتكرر بنفس مجموعة الوسائط eventName
و listener
، يُضاف ويستدعى المنصت في كل مرة.
server.prependListener('connection', (stream) => {
console.log('someone connected!');
});
تعيد إشارةً إلى كائن EventEmitter
، بحيث يمكن ربط الاستدعاءات كسلسلة (chain).
emitter.prependOnceListener(eventName, listener)
أضيف في الإصدار 6.0.0.
eventName
<string> | <symbol>: اسم الحدث المُنصَت إليه.listener
<Function>: دالة رد النداء (الاستدعاء).- القيمة المعادة: <EventEmitter>.
تضيف دالة listener
منصت لمرة واحدة إلى بداية مصفوفة منصتات الحدث المعني eventName
. بحيث يُلغى المنصت عند إطلاق الحدث eventName
ثم يُستدعى بعدئذٍ.
server.prependOnceListener('connection', (stream) => {
console.log('Ah, we have our first user!');
});
يعيد إشارة إلى كائن EventEmitter
، بحيث يمكن ربط الاستدعاءات.
emitter.removeAllListeners([eventName])
أضيف في الإصدار 0.1.26.
تزيل جميع المنصتات، أو تلك المنصتات المخصصة للحدث المعني eventName
.
لا يُنصح بإزالة المنصتات التي أُضيفت في مكان آخر من النص البرمجي، خاصة عند إنشاء كائن EventEmitter
من قبل مكون أو وحدة أخرى (مثل المقابس [socketsٍ أو مجاري الملفات [file streams]).
يعيد إشارة إلى كائن EventEmitter
، بحيث يمكن ربط الاستدعاءات كسلسلة (chain).
emitter.removeListener(eventName, listener)
أضيف في الإصدار: 0.1.26.
eventName
<string> | <symbol>: اسم الحدث المُنصَت إليه.listener
<Function>: دالة رد النداء (الاستدعاء).- القيمة المعادة:
<EventEmitter>
.
يزيل المنصت المعني listener
من مصفوفة المنصتات للحدث eventName
.
const callback = (stream) => {
console.log('someone connected!');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback);
يزيل هذا التابع ظهور واحد على الأكثر للمنصت من قائمة المنصتات، بالتالي إذا أضيف منصت عدة مرات إلى مصفوفة منصتات الحدث المعني eventName
، فسيتطلب الأمر استدعاء التابع removeListener()
عدة مرات لإزالة كل مرة ظهور.
لاحظ أنه عند إطلاق الحدث، ستستدعى المنصتات المرفقة به بالترتيب في وقت الإطلاق. بالتالي أي استدعاء للتابع removeListener()
أو removeAllListeners()
بعد الإطلاق وقبل الانتهاء من تنفيذ المنصت الأخير لن يزيلها من الحدث الجاري إطلاقه emit()
. أما الأحداث التالية فستتصرف كما هو متوقع.
const myEmitter = new MyEmitter();
const callbackA = () => {
console.log('A');
myEmitter.removeListener('event', callbackB);
};
const callbackB = () => {
console.log('B');
};
myEmitter.on('event', callbackA);
myEmitter.on('event', callbackB);
// يزيل كائن callbackA المنصت callbackB لكن سيُستدعى مع ذلك.
// مصفوفة المنصتات الداخلية في وقت الإطلاق [callbackA, callbackB]
myEmitter.emit('event');
// تطبع:
// A
// B
// أزيل منصت callbackB الآن.
// مصفوفة المنصتات الداخلية [callbackA]
myEmitter.emit('event');
// تطبع:
// A
لأن المنصتات تُدار بقائمة داخلية، فإن استدعاء هذه الطريقة سيغير من فهرس مكان أي منصت مسجل بعد المنصت المحذوف. هذا لا يغير في ترتيب استدعاء المنصتات، ولكنه يعني أن أي نسخة من مصفوفة المنصتات التي تعيدها الطريقة emitter.listeners()
يجب تحديثها.
يعيد هذا التابع إشارة إلى كائن EventEmitter
، بحيث يمكن ربط الاستدعاءات كسلسلة (chain).
emitter.setMaxListeners(n)
أضيف في الإصدار: 0.3.5.
- n: <integer>
- القيمة المعادة: <EventEmitter>.
تطبع كائنات EventEmitter
تحذيرًا افتراضيًا عندما يتجاوز عدد المنصتات المضافة إلى حدث معين 10 منصتات. هذه قيمة افتراضية مفيدة في العثور على تسريبات الذاكرة. لكن بالطبع، لا يجب أن تكون جميع الأحداث محددة فقط بعشر منصتات. وهنا يظهر التابع emitter.setMaxListeners()
الذي يتيح تعديل الحد الأقصى لكائن EventEmitter
معين. يمكنك وضع القيمة Infinity
(أو 0) لتحديد عدد لا نهائي من المنصتات.
يعيد التابع إشارة إلى كائن EventEmitter
، بحيث يمكن ربط الاستدعاءات كسلسلة (chain).
emitter.rawListeners(eventName)
أضيف في الإصدار 9.4.0.
eventName
<string> | <symbol>: اسم الحدث المُنصَت إليه.- القيمة المعادة: <Function[]>.
إعادة نسخة من مصفوفة منصتات الحدث المعني eventName
، شاملة أي مغلفات (wrappers، مثل تلك المنشأة من التابع .once()
).
const emitter = new EventEmitter();
emitter.once('log', () => console.log('log once'));
// تعود بمصفوفة جديدة لدالة ‘onceWrapper’ مع خاصية
// المنصت listener التي تحتوي رابط المنصت الأصلي بالأعلى
const listeners = emitter.rawListeners('log');
const logFnWrapper = listeners[0];
// ترمي عبارة “log once” ولا تفك حدث ‘once’
logFnWrapper.listener();
// ترمي عبارة “log once” وتزيل المنصت
logFnWrapper();
emitter.on('log', () => console.log('log persistently'));
// تعود بمصفوفة جديدة لدالة فردية مربوطة بدالة ‘.on()’ المذكورة بالأعلى
const newListeners = emitter.rawListeners('log');
// ترمي عبارة ‘log persistenlty’ مرتين
newListeners[0]();
emitter.emit('log');