از طریق منوی جستجو مطلب مورد نظر خود در وبلاگ را به سرعت پیدا کنید
مدیریت رویدادها در Node.js با EventEmitter در این آموزش، ما قصد داریم نگاهی به کلاس EventEmitter بومی Node بیندازیم. شما در مورد رویدادها، کارهایی که می توانید با EvenEmitter انجام دهید و روش استفاده از رویدادها در برنامه خود یاد خواهید گرفت. همچنین ماژول های بومی دیگر را از کلاس EventEmitter و چند نمونه تا…
سرفصلهای مطلب
معرفی
در این آموزش قصد داریم نگاهی به بومی Node بیاندازیم EventEmitter
کلاس شما در مورد رویدادها، آنچه که می توانید با آن انجام دهید، یاد خواهید گرفت EvenEmitter
و روش استفاده از رویدادها در برنامه خود.
همچنین ماژولهای بومی دیگر را پوشش خواهیم داد EventEmitter
کلاس و چند مثال برای درک آنچه در پشت صحنه می گذرد.
بنابراین به طور خلاصه، ما تقریباً هر آنچه را که باید در مورد آن بدانید را پوشش خواهیم داد EventEmitter
کلاس
ما در این آموزش از برخی ویژگی های اساسی ES6 مانند کلاس های جاوا اسکریپت و توابع پیکان استفاده خواهیم کرد. اگر دانش قبلی از نحو ES6 دارید، مفید است، اما اجباری نیست.
یک رویداد چیست؟
یک الگوی نرم افزاری کامل حول رویدادها و استفاده از آنها می چرخد. معماری رویداد محور امروزه نسبتاً رایج است و برنامه های کاربردی رویداد محور انواع مختلفی از رویدادها را تولید، شناسایی و واکنش نشان می دهند.
می توان گفت که هسته Node.js تا حدی مبتنی بر رویداد است زیرا بسیاری از ماژول های بومی مانند سیستم فایل (fs
) و stream
ماژول به صورت نوشته شده است EventEmitter
خودشون
در برنامه نویسی رویداد محور، یک رویداد نتیجه یک یا چند عمل است. به عنوان مثال، این می تواند یک اقدام کاربر یا یک خروجی دوره ای از یک حسگر باشد.
میتوانید برنامههای رویداد محور را بهعنوان مدلهای انتشار-اشتراک مشاهده کنید که در آن یک ناشر رویدادها را راهاندازی میکند و مشترکان به آنها گوش میدهند و مطابق با آن عمل میکنند.
به عنوان مثال، فرض کنید یک سرور تصویر داریم که کاربران می توانند تصاویر را در آن آپلود کنند. در برنامه نویسی رویداد محور، اقدامی مانند آپلود تصویر یک رویداد را منتشر می کند. برای استفاده از آن نیز وجود خواهد داشت 1..n
مشترکین آن رویداد
هنگامی که رویداد آپلود فعال شد، یک مشترک می تواند با ارسال یک ایمیل به مدیر وب سایت به آن واکنش نشان دهد و به آنها اطلاع دهد که یک کاربر عکسی را آپلود کرده است. مشترک دیگری ممکن است اطلاعات مربوط به اقدام را جمع آوری کند و آنها را در پایگاه داده باقی بماند.
این رویدادها معمولاً مستقل از یکدیگر هستند، اگرچه ممکن است وابسته نیز باشند.
Event Emitter چیست؟
را EventEmitter
کلاس یک کلاس داخلی است که در آن قرار دارد events
مدول. با توجه به مستندات:
بخش عمده ای از Node.js هسته API حول یک معماری ناهمزمان رویداد محور اصطلاحی ساخته شده است که در آن انواع خاصی از اشیاء (موسوم به “امیتر”) رویدادهای نامگذاری شده را منتشر می کنند که باعث می شود
Function
اشیاء (“شنوندگان”) که باید فراخوانی شوند”
این کلاس تا حدی می تواند به عنوان یک پیاده سازی کمکی از مدل pub/sub توصیف شود زیرا کمک می کند ساطع کننده های رویداد (ناشران) برای انتشار مناسبت ها (پیام ها) و شنوندگان (مشترکین) اقدام کنند روی این رویدادها – به روشی ساده.
ایجاد EventEmitters
همانطور که گفته شد، بیایید جلو برویم و یک ایجاد کنیم EventEmitter
. این کار را می توان از طریق ایجاد یک نمونه از خود کلاس یا با پیاده سازی آن از طریق یک کلاس سفارشی و سپس ایجاد یک نمونه از آن کلاس انجام داد.
ایجاد یک Event Emitter هدف – شی
بیایید با یک شی ساطع کننده رویداد ساده شروع کنیم. ما ایجاد خواهیم کرد EventEmitter
که هر ثانیه یک رویداد منتشر می کند که حاوی اطلاعاتی در مورد زمان آپدیت برنامه است.
اولین، import را EventEmitter
کلاس از events
ماژول ها:
const { EventEmitter } = require('events');
سپس بیایید یک را ایجاد کنیم EventEmitter
:
const timerEventEmitter = new EventEmitter();
انتشار یک رویداد از این شی به همین سادگی است:
timerEventEmitter.emit("update");
ما نام رویداد را مشخص کرده ایم و آن را به عنوان یک رویداد منتشر کرده ایم. هیچ اتفاقی نمی افتد زیرا هیچ شنونده ای برای واکنش به این رویداد وجود ندارد. بیایید کاری کنیم که این رویداد هر ثانیه تکرار شود.
با استفاده از setInterval()
روش، یک تایمر ایجاد می شود که منتشر می کند update
رویداد در هر ثانیه:
let currentTime = 0;
// This will trigger the update event each passing second
setInterval(() => {
currentTime++;
timerEventEmitter.emit('update', currentTime);
}, 1000);
را EventEmitter
نمونه یک نام رویداد و یک مجموعه دلخواه از آرگومان ها را می پذیرد. در این مورد، ما گذشت eventName
مانند update
و currentTime
به عنوان زمان از شروع برنامه.
ما امیتر را از طریق فعال می کنیم emit()
روش، که رویداد را با اطلاعاتی که ما ارائه کردهایم پیش میبرد.
با آماده شدن پخش کننده رویداد، بیایید یک شنونده رویداد را در آن مشترک کنیم:
timerEventEmitter.روی('update', (time) => {
console.log('Message Received from publisher');
console.log(`${time} seconds passed since the program started`);
});
با استفاده از روی()
روش، ارسال نام رویداد برای تعیین اینکه میخواهیم شنونده را به کدام یک متصل کنیم، به ما امکان میدهد شنونده ایجاد کنیم. بر را update
رویداد، متدی اجرا می شود که زمان را ثبت می کند. میتوانید بارها و بارها همان شنونده را اضافه کنید و هر کدام مشترک رویداد شوند.
استدلال دوم از روی()
تابع یک تماس برگشتی است که می تواند هر تعداد از داده های اضافی را که توسط رویداد منتشر شده است را بپذیرد. هر شنونده می تواند پس از حفظ سفارش، داده های مورد نظر خود را انتخاب کند.
اجرای این اسکریپت باید نتیجه دهد:
Message Received from publisher
1 seconds passed since the program started
Message Received from publisher
2 seconds passed since the program started
Message Received from publisher
3 seconds passed since the program started
...
در مقابل، ما می توانیم استفاده کنیم once()
روش اشتراک – اگر نیاز دارید چیزی را فقط در اولین باری که یک رویداد راهاندازی میکند اجرا کنید:
timerEventEmitter.once('update', (time) => {
console.log('Message Received from publisher');
console.log(`${time} seconds passed since the program started`);
});
با اجرای این کد به دست می آید:
Message Received from publisher
1 seconds passed since the program started
Event Emitter با چندین شنونده
حالا بیایید با سه شنونده، نوع متفاوتی از رویداد منتشر کنیم. این یک شمارش معکوس خواهد بود. یک شنونده کاربر را به روز می کند روی در هر ثانیه، زمانی که شمارش معکوس به پایان می رسد، یک شنونده به کاربر اطلاع می دهد و پس از پایان شمارش معکوس، آخرین شنونده فعال می شود:
update
– این رویداد آغاز خواهد شد روی در هر ثانیهend
– این رویداد در پایان شمارش معکوس فعال می شودend-soon
– این رویداد 2 ثانیه قبل از پایان شمارش معکوس فعال می شود
بیایید تابعی ایجاد کنیم که این رویداد-امیتر را ایجاد کرده و آن را برگرداند:
const countDown = (countdownTime) => {
const eventEmitter = new EventEmitter();
let currentTime = 0;
// This will trigger the update event each passing second
const timer = setInterval(() => {
currentTime++;
eventEmitter.emit('update', currentTime);
// Check if countdown has reached to the end
if (currentTime === countdownTime) {
clearInterval(timer);
eventEmitter.emit('end');
}
// Check if countdown will end in 2 seconds
if (currentTime === countdownTime - 2) {
eventEmitter.emit('end-soon');
}
}, 1000);
return eventEmitter;
};
در این تابع، یک رویداد مبتنی بر بازه را شروع کرده ایم که نشان می دهد update
رویداد در فاصله زمانی یک ثانیه
در ابتدا if
در شرایط، بررسی می کنیم که آیا شمارش معکوس به پایان رسیده است و رویداد مبتنی بر فاصله را متوقف می کنیم. اگر چنین است، ما یک را شلیک می کنیم end
رویداد.
در شرط دوم، بررسی می کنیم که آیا شمارش معکوس 2 ثانیه تا پایان باقی مانده است یا خیر، و آن را منتشر می کنیم end-soon
رویداد اگر چنین است.
اکنون، اجازه دهید چند مشترک را به این امیتر رویداد اضافه کنیم:
const myCountDown = countDown(5);
myCountDown.روی('update', (t) => {
console.log(`${t} seconds since the timer started`);
});
myCountDown.روی('end', () => {
console.log('Countdown is completed');
});
myCountDown.روی('end-soon', () => {
console.log('Count down will end in 2 seconds');
});
این کد باید نتیجه دهد:
1 seconds has been passed since the timer started
2 seconds has been passed since the timer started
3 seconds has been passed since the timer started
Count down will end in 2 seconds
4 seconds has been passed since the timer started
5 seconds has been passed since the timer started
Countdown is completed
در حال تمدید Event Emitter
در این بخش، بیایید با گسترش دادن یک رویداد Emitter با همان عملکرد بسازیم EventEmitter
کلاس ابتدا a ایجاد کنید CountDown
کلاسی که رویدادها را مدیریت خواهد کرد:
const { EventEmitter } = require('events');
class CountDown extends EventEmitter {
constructor(countdownTime) {
super();
this.countdownTime = countdownTime;
this.currentTime = 0;
}
startTimer() {
const timer = setInterval(() => {
this.currentTime++;
this.emit('update', this.currentTime);
// Check if countdown has reached to the end
if (this.currentTime === this.countdownTime) {
clearInterval(timer);
this.emit('end');
}
// Check if countdown will end in 2 seconds
if (this.currentTime === this.countdownTime - 2) {
this.emit('end-soon');
}
}, 1000);
}
}
همانطور که می بینید، ما می توانیم استفاده کنیم this.emit()
داخل کلاس مستقیم همچنین startTimer()
از تابع استفاده می شود تا به ما امکان کنترل زمان شروع شمارش معکوس را بدهد. در غیر این صورت، به محض ایجاد شی شروع می شود.
بیایید یک شی جدید از ایجاد کنیم CountDown
و در آن مشترک شوید:
const myCountDown = new CountDown(5);
myCountDown.روی('update', (t) => {
console.log(`${t} seconds has been passed since the timer started`);
});
myCountDown.روی('end', () => {
console.log('Countdown is completed');
});
myCountDown.روی('end-soon', () => {
console.log('Count down will be end in 2 seconds');
});
myCountDown.startTimer();
اجرای این به این نتیجه می رسد:
1 seconds has been passed since the timer started
2 seconds has been passed since the timer started
3 seconds has been passed since the timer started
Count down will be end in 2 seconds
4 seconds has been passed since the timer started
5 seconds has been passed since the timer started
Countdown is completed
یک نام مستعار برای روی()
تابع است addListener()
. در نظر بگیرید end-soon
شنونده رویداد:
myCountDown.روی('end-soon', () => {
console.log('Count down will be end in 2 seconds');
});
ما هم می توانستیم همین کار را انجام دهیم addListener()
مثل این:
myCountDown.addListener('end-soon', () => {
console.log('Count down will be end in 2 seconds');
});
هر دو کار می کنند. آنها تقریباً مانند مترادف هستند. با این حال، اکثر کدنویسان ترجیح می دهند از آن استفاده کنند روی()
.
توابع مهم از Event Emitter
بیایید به برخی از عملکردهای مهمی که می توانیم استفاده کنیم نگاهی بیندازیم روی EventEmitter
س
رویدادNames()
این تابع همه نامهای شنونده فعال را به صورت آرایه برمیگرداند:
const myCountDown = new CountDown(5);
myCountDown.روی('update', (t) => {
console.log(`${t} seconds has been passed since the timer started`);
});
myCountDown.روی('end', () => {
console.log('Countdown is completed');
});
myCountDown.روی('end-soon', () => {
console.log('Count down will be end in 2 seconds');
});
console.log(myCountDown.eventNames());
اجرای این کد منجر به موارد زیر می شود:
( 'update', 'end', 'end-soon' )
اگر قرار بود در رویداد دیگری مشترک شویم مانند myCount.روی('some-event', ...)
، رویداد جدید نیز به آرایه اضافه خواهد شد.
به خاطر داشته باشید که این روش رویدادهای منتشر شده را بر نمی گرداند. فهرستی از رویدادهایی که در آن مشترک شده اند را برمی گرداند.
removeListener()
همانطور که از نام آن پیداست، این تابع یک کنترل کننده مشترک را از یک حذف می کند EventEmitter
:
const { EventEmitter } = require('events');
const emitter = new EventEmitter();
const f1 = () => {
console.log('f1 Triggered');
}
const f2 = () => {
console.log('f2 Triggered');
}
emitter.روی('some-event', f1);
emitter.روی('some-event', f2);
emitter.emit('some-event');
emitter.removeListener('some-event', f1);
emitter.emit('some-event');
پس از شروع اولین رویداد، زیرا هر دو f1
و f2
فعال هستند – هر دو تابع اجرا خواهند شد. پس از آن، ما حذف شده است f1
از EventEmitter
. فقط زمانی که رویداد را دوباره منتشر می کنیم f2
اجرا خواهد کرد:
f1 Triggered
f2 Triggered
f2 Triggered
یک نام مستعار برای removeListener()
است off()
. مثلاً میتوانستیم بنویسیم:
emitter.removeListener('some-event', f1);
مانند:
emitter.off('some-event', f1);
هر دو اثر یکسانی دارند.
removeAllListeners()
باز هم همانطور که از نام آن پیداست – این تابع همه شنوندگان را از تمام رویدادهای an حذف می کند EventEmitter
:
const { EventEmitter } = require('events');
const emitter = new EventEmitter();
const f1 = () => {
console.log('f1 Triggered');
}
const f2 = () => {
console.log('f2 Triggered');
}
emitter.روی('some-event', f1);
emitter.روی('some-event', f2);
emitter.emit('some-event');
emitter.removeAllListeners();
emitter.emit('some-event');
اولین emit()
هر دو را اخراج خواهد کرد f1
و f2
از آنجایی که آنها در آن زمان فعال هستند. پس از حذف آنها، emit()
تابع رویداد را منتشر می کند، اما هیچ شنونده ای به آن پاسخ نمی دهد:
f1 Triggered
f2 Triggered
رسیدگی به خطا
اگر می خواهید با خود خطایی صادر کنید EventEmitter
، باید با یک انجام شود error
نام رخداد. این استاندارد برای همه است EventEmitter
اشیاء در Node.js. این رخداد باید همچنین با یک Error
هدف – شی. به عنوان مثال، یک رویداد خطا می تواند به صورت زیر منتشر شود:
myEventEmitter.emit('error', new Error('Something bad happened'));
هر شنونده ای برای error
رویداد باید یک callback با یک آرگومان برای گرفتن آن داشته باشد Error
اعتراض کنید و با مهربانی آن را اداره کنید. اگر یک EventEmitter
یک را منتشر می کند error
رویداد، اما هیچ شنونده ای برای آن مشترک نشده است error
رویدادها، برنامه Node.js را پرتاب می کند Error
که منتشر شد.
این در نهایت Node.js را متوقف می کند process از اجرا و خروج از برنامه خود، در حالی که stacktrace برای خطا در نمایش داده می شود console.
بیایید فرض کنیم، در ما CountDown
کلاس، countdownTime
پارامتر نمی تواند کمتر از 2 باشد زیرا نمی توانیم رویداد را راه اندازی کنیم end-soon
در غیر این صورت.
در چنین حالتی، یک را منتشر کنیم error
رویداد:
class CountDown extends EventEmitter {
constructor(countdownTime) {
super();
if (countdownTimer < 2) {
this.emit('error', new Error('Value of the countdownTimer cannot be less than 2'));
}
this.countdownTime = countdownTime;
this.currentTime = 0;
}
// ...........
}
مدیریت این خطا مانند سایر رویدادها انجام می شود:
myCountDown.روی('error', (err) => {
console.error('There was an error:', err);
});
داشتن یک شنونده همیشه تمرین خوبی است error
مناسبت ها.
استفاده از ماژول های بومی Event Emitter
بسیاری از ماژول های بومی در Node.js گسترش می دهند EventEmitter
طبقه و در نتیجه خود ساطع کننده رویداد هستند.
یک مثال عالی است Stream
کلاس در اسناد رسمی آمده است:
جریان ها می توانند خوانا، قابل نوشتن یا هر دو باشند. همه جریان ها نمونه هایی از
EventEmitter
.
بیایید نگاهی به برخی کلاسیک بیندازیم Stream
استفاده:
const fs = require('fs');
const writer = fs.createWriteStream('example.txt');
for (let i = 0; i < 100; i++) {
writer.write(`hello, #${i}!\n`);
}
writer.روی('finish', () => {
console.log('All writes are now complete.');
});
writer.end('This is the end\n');
با این حال، بین عملیات نوشتن و writer.end()
تماس بگیرید، یک شنونده اضافه کرده ایم. Stream
s منتشر می کند a finished
رویداد پس از اتمام رویدادهای دیگر، مانند error
، pipe
و unpipe
هنگامی که یک خطا رخ می دهد یا یک جریان خواندن به یک جریان نوشتن لوله می شود یا از آن خارج می شود، منتشر می شوند.
یکی دیگر از کلاس های قابل توجه است child_process
کلاس و آن spawn()
روش:
const { spawn } = require('child_process');
const ls = spawn('ls', ('-lh', '/usr'));
ls.stdout.روی('data', (data) => {
console.log(`stdout: ${data}`);
});
ls.stderr.روی('data', (data) => {
console.error(`stderr: ${data}`);
});
ls.روی('close', (code) => {
console.log(`child process exited with code ${code}`);
});
وقتی که child_process
در لوله خروجی استاندارد می نویسد data
رویداد از stdout
(که همچنین extends EventEmitter
) شلیک خواهد کرد. هنگامی که جریان خروجی با خطا مواجه می شود، data
رویداد از stderr
لوله
در نهایت، پس از process خروجی ها، close
رویداد اخراج می شود.
نتیجه
معماری رویداد محور به ما اجازه می دهد تا سیستم هایی ایجاد کنیم که هستند جدا شده ولی بسیار منسجم. رویدادها نتیجه یک عمل معین را نشان می دهند و 1..n
شنوندگان را می توان برای گوش دادن و واکنش به آنها تعریف کرد.
در این مقاله، ما به این موضوع پرداختیم EventEmitter
کلاس و عملکرد آن ما آن را نمونهسازی کردهایم و مستقیماً از آن استفاده کردهایم، و همچنین رفتار آن را به یک شی سفارشی گسترش دادهایم.
در نهایت، برخی از عملکردهای قابل توجه کلاس را پوشش داده ایم.
مثل همیشه کد منبع موجود است روی GitHub.
(برچسبها برای ترجمه)# جاوا اسکریپت
منتشر شده در 1403-01-21 04:04:05