Джедайські прийоми на JavaScript: магічні властивості транслятора подій


Дізнайтесь більше про нові кар'єрні можливості в EchoUA. Цікаві проекти, ринкова оплата, гарний колектив. Надсилайте резюме та приєднуйтеся до нас.

Про що ми?

Event Emitter можна перекласти як “транслятор” або “емітер” подій. Звучить як назва пристрою, що вміє генерувати подію, яку може “почути” будь-хто.

Уявіть собі таку схему: у Вашому асинхронному коді певна ділянка може “крикнути” іншим, що вона виконала свою задачу, а інші частини “почують” цей сигнал і вживуть відповідних заходів.

Event Emitter – це шаблон, який можна реалізувати за допомогою різних способів. Основна ідея полягає в тому, щоб грамотно створити основу для управління подіями і реалізувати можливість будь-яким елементам “підписатися” на нього (і бути в курсі того, що відбувається). З іншими шаблонами проектування Ви можете ознайомитися в нашій статті.

Вже цікаво, як така магія може працювати? Отже, ми хочемо добитися коду, який потім можна буде використати так:

let input = document.querySelector ('input[type=" text"]');let button = document.querySelector ('button');let h1 = document.querySelector ('h1');button.addEventListener ('click', () => { emitter.emit ('event:name-changed', {name: input.value});});let emitter = new EventEmitter ();emitter.subscribe ('event:name-changed', data => { h1.innerHTML = 'Your name is: ${data.name}';});

Почнемо.

Реалізація

class EventEmitter { constructor (){ this.events ={}; }}

Як бачите, конструктор нашого класу ініціалізує поле events, поки що роблячи його порожнім об’єктом. Задача цього поля – зберігати події, “що підписалися” на нас (тобто в ньому зберігатимуться функції).

Метод subscribe:

subscribe ( eventName, fn ) { if ( !this.events[eventName] ) { this.events[eventName] = []; } this.events[eventName].push (fn);}

Цей метод приймає як аргументи назву події (наприклад, event:name-changed, як в нашому прикладі) й функцію, яка викликатиметься, коли ініціюватиметься трансльована подія.

Одна з ключових особливостей функцій в JavaScript полягає в тому, що функції – це “об’єкти першого класу”, тобто ми можемо передати функцію як параметр іншої функції, як метод subscribe ().

Метод emit:

emit (eventName, data) { const event = this.events[eventName]; if ( event ) { event.forEach (fn => { fn.call (null, data); }); }}

Цей метод приймає ім’я події, яку ми хочемо всім транслювати, і дані, які вирушатимуть у момент цієї події. Якщо в екземплярі класу збережені якісь підписані на нього події, ми проходимося по кожній з них і викликаємо кожну, передаючи їй дані, які хочемо транслювати.

Власне, з реалізацією патерну ми закінчили. Залишається ще одна проблема: треба буде “відписати” функції, які нам більше не потрібні. Не зробимо цього – зіткнемося з витоком пам’яті.

Давайте розв’яжемо цю проблему. Нехай метод subscribe () повертає функцію unsubscribe (), яку пізніше можна буде використати, щоб відписатися від події.

subscribe (eventName, fn) { if (!this.events[eventName]) { this.events[eventName] = []; } this.events[eventName].push (fn); return () => { this.events[eventName] = this.events[eventName].filter (eventFn => fn !== eventFn); }}

Як ми пам’ятаємо, в JavaScript особливі функції – їх легко можна повернути з іншої функції. Тепер метод subscribe () можна використати таким чином:

let unsubscribe = emitter.subscribe ('event:name-changed', data => console.log (data));unsubscribe ();

Викликаючи збережену в змінній функцію unsubscribe (), ми відписуємося від події.

От і все. Бувайте, витоки пам’яті!

Ось тут можна спробувати весь створений код у дії.

Джерело: Medium

Київ, Харків, Одеса, Дніпро, Запоріжжя, Кривий Ріг, Вінниця, Херсон, Черкаси, Житомир, Хмельницький, Чернівці, Рівне, Івано-Франківськ, Кременчук, Тернопіль, Луцьк, Ужгород, Кам'янець-Подільський, Стрий - за статистикою саме з цих міст програмісти найбільше переїжджають працювати до Львова. А Ви розглядаєте relocate?


Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *