Menu

EventEmitter ב-Node.js: on, emit, once ואירועים משלכם

EventEmitter מהמודול events של Node מאפשר לאובייקט להכריז על אירועים עם שם בעזרת emit, ולקוד אחר להגיב בעזרת on או once. ה-listeners רצים באופן סינכרוני, לפי הסדר שבו הם נוספו.

בדף הזה יש עורכים שאפשר להריץ. לערוך, להריץ ולראות את הפלט מיד.

EventEmitter היא המחלקה במודול events של Node שמאפשרת לאובייקט להכריז על אירועים עם שם. רושמים listener עם emitter.on('name', fn) ומפעילים אותו עם emitter.emit('name', ...args); כל listener של השם הזה רץ, לפי הסדר שבו הוא נוסף, עם הארגומנטים שהעברתם. שרתי HTTP, ‏streams ו-process הם כולם EventEmitters, כך שאותן כמה מתודות עובדות בכל מקום ב-Node.

שני ה-listeners רצים בין before emit ל-after emit: ‏emit קורא להם באופן סינכרוני וחוזר רק כשהם סיימו. החליפו את הסדר של שתי הקריאות ל-shop.on, ושתי השורות האמצעיות יתחלפו גם הן.

on, ‏once ו-off

on שומר listener עד שמסירים אותו, once מסיר אותו אחרי הקריאה הראשונה, ו-off (זהה ל-removeListener) מסיר אחד. כדי להסיר listener צריך את אותה פונקציה שהוספתם, אז תנו לה שם במקום להעביר arrow function ישירות.

first open ever מודפס פעם אחת, אחרי Ada. ברגע ש-logOpen מוסר אף אחד לא מאזין, ולכן ה-emit האחרון מחזיר false; ‏door.removeAllListeners('open') היה מנקה את שני ה-listeners בקריאה אחת.

מתודהמה היא עושה
on(name, fn), addListenerקוראת ל-fn בכל emit
once(name, fn)קוראת ל-fn רק ב-emit הבא
prependListener(name, fn)כמו on, אבל רצה לפני ה-listeners הקיימים
off(name, fn), removeListenerמסירה את הפונקציה הזו
emit(name, ...args)קוראת ל-listeners, מחזירה true אם היו כאלה
listenerCount(name)כמה listeners יש לאירוע

אירוע ה-error

'error' הוא אירוע מיוחד. אם פולטים אותו ואף אחד לא מאזין, emit זורק את השגיאה, ובתוכנית אמיתית זריקה שלא נתפסה מפילה את התהליך. תמיד הוסיפו listener ל-'error' ב-emitters שיכולים להיכשל, כמו streams ושרתים.

בלי listener, ‏emit זורק וה-catch תופס את זה; עם ה-listener, אותו emit פשוט קורא לו והתוכנית ממשיכה. כל שם אירוע אחר בלי listeners פשוט מתעלמים ממנו בשקט.

הרחבת EventEmitter במחלקה משלכם

הדרך המקובלת להשתמש בו היא לגרום למחלקה שלכם לרשת מ-EventEmitter, לקרוא ל-super() ב-constructor ול-this.emit כשמשהו קורה. מי שמשתמש באובייקט נרשם אליו כמו לכל אובייקט של Node.

הוא מדפיס 25%, 50%, 75%, 100% ואז photo.png uploaded. העבירו את up.start() אל מעל שתי שורות ה-up.on ושום דבר לא יודפס: האירועים נפלטים לפני שמישהו מאזין.

אותה מלכודת קיימת כשפולטים אירוע מתוך constructor. ספריות שחייבות לפלוט מיד עוטפות את הקריאה ב-process.nextTick, שרץ אחרי שורות ה-on של הקוד הקורא (ראו את הדף על ה-event loop).

לחכות לאירוע עם events.once

events.once(emitter, name) מחזיר promise שמתממש עם הארגומנטים של האירוע, כמערך, בפעם הבאה שהוא נפלט. הוא הופך אירוע חד פעמי למשהו שאפשר לעשות לו await, והוא נדחה אם ה-emitter פולט 'error' קודם.

ה-await מחכה עד שהטיימר פולט ready, ואז המערך מתפרק לשני הארגומנטים. לאירוע שנפלט הרבה פעמים, events.on(emitter, name) נותן async iterator שאפשר לעבור עליו בלולאה עם for await.

טעויות נפוצות

הוספת listener בתוך פונקציה שרצה הרבה. קריאה ל-on בכל request handler צוברת listeners וזיכרון. הוסיפו אותו פעם אחת, או השתמשו ב-once.

הסרה של פונקציה שנכתבה ישירות בקריאה. off('data', () => ...) לא מסיר כלום, כי ה-arrow function הזו היא פונקציה חדשה. שמרו הפניה לפונקציה שהוספתם.

לצפות ש-listeners ירוצו מאוחר יותר. emit הוא סינכרוני, כך ש-listener איטי מעכב את הקוד שפלט את האירוע. לסמן את ה-listener כ-async לא עוזר, כי הקוד שלו עדיין רץ על אותו thread עד ה-await הראשון. חלקו עבודה כבדה לחלקים עם setImmediate ביניהם, או העבירו אותה ל-worker thread.

שאלות נפוצות

האם EventEmitter אסינכרוני?

לא. emit קורא לכל listener מיד, אחד אחרי השני, וחוזר כשהאחרון סיים. קוד בתוך listener יכול להתחיל עבודה אסינכרונית, אבל ה-listener עצמו נקרא באופן סינכרוני.

מה המשמעות של האזהרה MaxListenersExceededWarning?

Node מזהיר כשמוסיפים יותר מ-10 listeners לאירוע אחד, כי זה הרבה פעמים אומר ש-listener נוסף בכל בקשה ואף פעם לא מוסר. הסירו את ה-listeners העודפים, או קראו ל-emitter.setMaxListeners(n) אם אתם באמת צריכים יותר.

מה emit מחזיר?

true אם לאירוע היה לפחות listener אחד, ו-false אם אף אחד לא האזין. הוא לא מחזיר את מה שה-listeners מחזירים.

אילו אובייקטים ב-Node.js הם EventEmitters?

רוב אובייקטי הליבה של Node: שרתי HTTP, בקשות ותשובות, streams, ‏sockets, ‏process וממשקי readline. בגלל זה לכולם יש on, once ו-off.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל