Как да използваме събитията в Node.js по правилния начин

Преди програмирането, управлявано от събития, да стане популярно, стандартният начин за комуникация между различни части на приложението беше доста ясен: компонент, който искаше да изпрати съобщение до друг, изрично извика метод за този компонент. Но управляваният от събития код е написан, за да реагира, вместо да бъде извикан .

Ползите от събитието

Този подход кара нашите компоненти да бъдат много по-отделени . Докато продължаваме да пишем заявление, ние идентифицираме събития по пътя. Уволняваме ги в точното време и прикачваме един или повече слушатели на събития към всеки един. Разширяването на функционалността става много по-лесно. Можем да добавим повече слушатели към определено събитие. Не се намесваме в съществуващите слушатели или в частта от приложението, от която е стартирано събитието. Това, за което говорим, е моделът на наблюдателя.

Проектиране на управлявана от събития архитектура

Идентифицирането на събития е доста важно. Не искаме в крайна сметка да се налага да премахваме / заместваме съществуващите събития от системата. Това може да ни принуди да изтрием / променим произволен брой слушатели, които са били прикачени към събитието. Общият принцип, който използвам, е да обмисля задействане на събитие само когато единица бизнес логика завърши изпълнението.

Така че кажете, че искате да изпратите куп различни имейли след регистрацията на потребителя. Сега самият процес на регистрация може да включва много сложни стъпки и заявки. Но от бизнес гледна точка това е една стъпка. И всеки от имейлите, които трябва да бъдат изпратени, също са индивидуални стъпки. Така че би имало смисъл да се задейства събитие веднага щом регистрацията приключи. Към него има няколко слушатели, всеки отговорен за изпращането на един тип имейл.

Асинхронната, управлявана от събития архитектура на Node има определени видове обекти, наречени „излъчватели“. Те излъчват именувани събития, които карат функциите, наречени „слушатели“, да бъдат извикани. Всички обекти, които излъчват събития, са екземпляри от класа EventEmitter. Използвайки го, можем да създадем свои собствени събития:

Пример

Нека използваме вградения модул за събития (който ви препоръчвам да разгледате подробно), за да получите достъп EventEmitter.

Това е частта от приложението, където нашият сървър получава HTTP заявка, записва нов потребител и излъчва събитие:

И отделен модул, към който прикачваме слушател:

Добра практика е да отделяте политиката от прилагането. В този случай политиката означава кои слушатели са абонирани за кои събития. Внедряването означава самите слушатели.

Това разделяне позволява на слушателя да стане многократно използваем също. Той може да бъде прикачен към други събития, които изпращат същото съобщение (потребителски обект). Също така е важно да се спомене, че когато множество слушатели са прикачени към едно събитие, те ще бъдат изпълнени синхронно в реда, в който са били прикачени . Следователно someOtherListenerще работи след sendEmailOnRegistrationзавършване на изпълнението.

Ако обаче искате вашите слушатели да работят асинхронно, можете просто да обгърнете техните имплементации по setImmediateследния начин:

Поддържайте слушателите си чисти

Придържайте се към принципа на единната отговорност, когато пишете слушатели. Един слушател трябва да прави само едно нещо и да го прави добре. Избягвайте, например, да пишете твърде много условности в слушател, които решават какво да правят в зависимост от данните (съобщението), предадени от събитието. В този случай би било много по-подходящо да се използват различни събития:

Отделяне на слушателите изрично, когато е необходимо

В предишния пример нашите слушатели бяха напълно независими функции. Но в случаите, когато слушател е свързан с обект (това е метод), той трябва да бъде отделен ръчно от събитията, за които се е абонирал. В противен случай обектът никога няма да бъде събран за боклук, тъй като част от обекта (слушателят) ще продължи да се препраща от външен обект (излъчвателя). По този начин възможността за изтичане на памет.

Например, ако изграждаме приложение за чат и искаме отговорността за показване на известие, когато в чат стая пристигне ново съобщение, към което потребителят се е свързал, трябва да лежи в самия потребителски обект, можем да направим това:

Когато потребителят затвори раздела си или загуби връзката си с интернет за известно време, естествено може да поискаме да задействаме обратно обаждане от страна на сървъра, което уведомява останалите потребители, че единият от тях току-що е офлайн. На този етап, разбира се, няма смисъл displayNewMessageNotificationда бъде извикан за офлайн потребителя. Той ще продължи да се извиква при нови съобщения, освен ако не го премахнем изрично. Ако не го направим, освен ненужното повикване, потребителският обект също ще остане в паметта за неопределено време. Така че не забравяйте да се обадите disconnectFromChatroomв обратното повикване от страна на сървъра, което се изпълнява, когато потребителят излезе офлайн.

Внимавай

Разхлабеното свързване в управлявани от събития архитектури също може да доведе до повишена сложност, ако не сме внимателни. Може да бъде трудно да се проследят зависимостите в нашата система. Нашето приложение ще стане особено податливо на този проблем, ако започнем да излъчваме събития от слушателите. Това може да предизвика вериги от неочаквани събития.