Сигнали за лаштунками

pic

Фото: Karsten Winegeart на Unsplash

Сигнали (Signals) пропонують численні переваги, включаючи вбудовану гранулярну реактивність, простий API, лаконічний і зрозумілий потік даних та багато іншого.

Саме тому вони використовуються в майже всіх основних фреймворках, таких як Solid, Qwix, Angular та Vue.

Вони стали настільки популярними, що наразі існує пропозиція додати їх безпосередньо в мову JavaScript:

[

GitHub - tc39/proposal-signals: Пропозиція додати сигнали до JavaScript.

Пропозиція додати сигнали до JavaScript. Внесіть свій вклад у розробку tc39/proposal-signals, створивши обліковий запис на…

github.com

](https://github.com/tc39/proposal-signals?source=post_page-----19cbcb6b802b--------------------------------)

Сигнали — це чудово, але як саме вони працюють за лаштунками?

В цій статті я використаю синтаксис SolidJs, але концепції застосовні до всіх фреймворків, які використовують сигнали.
Я також поясню спрощену версію реальних реалізацій.

Давайте почнемо з вивчення найпростішої одиниці у світі сигналів: Signal (Сигнал).

Signal (Сигнал)

Signal (Сигнал) — це реактивна структура даних, яка використовується для керування станом в нашій програмі.

Давайте створимо Signal!

pic

Ми створюємо коробку з значенням (в цьому випадку "John") і списком підписників.

Також ми повертаємо аксесор (в цьому випадку name) і сеттер (setName).

Аксесори (Accessors) надають спосіб отримувати дані всередині сигналу; ми не можемо безпосередньо отримати доступ до значення.

Сеттер (Setter) використовується для оновлення даних у Signal, оскільки безпосередній доступ заборонено.

Чудово, у нас є спосіб зберігати стан, але нам також потрібен спосіб реагувати на зміни цього стану. Для цього ми можемо використовувати Effect (Ефект).

Effect (Ефект)

Effect (Ефект) — це наш інструмент для реакції на зміни в сигналах.
Давайте використаємо один і подивимося, як це працює.

Ми створили дуже базовий Effect (Ефект), який викликає аксесор (accessor) name і виводить результат в консоль.

Ось наш маленький Effect (Ефект), що містить анонімну функцію.

pic

Щоб зрозуміти, що відбувається, коли ми створюємо Effect (Ефект), нам потрібно ввести контекст відслідковування (tracking context).

pic

Контекст відслідковування — це глобальний стек виконання, який допомагає відслідковувати те, що наразі виконується.

Коли ми створюємо Effect (Ефект), ми додаємо його в контекст відслідковування:

pic

Як тільки ми додаємо Effect (Ефект) в контекст відслідковування, ми виконуємо функцію, яку він містить.

pic

Чарівність відбувається, коли ми виконуємо будь-який Accessor (Аксесор) всередині createEffect.

Коли ми викликаємо Accessor (Аксесор), ми перевіряємо верхівку контексту відслідковування.
Тепер ми додаємо Effect (Ефект) на верхівку контексту відслідковування як підписника (subscriber) сигналу, який підключений до аксесора (accessor).

pic

pic

(Жовта зірочка позначає Effect (Ефект)).

Після того, як ми додали Effect (Ефект) як підписника, ми повертаємо значення, яке утримує Signal (Сигнал), в даному випадку — John.

Тоді ми виводимо John в консоль.

pic

І видаляємо Effect (Ефект) з контексту відслідковування:

pic

Отже, ми вивели в консоль.
У чому ж полягає основна ідея? Щоб зрозуміти це, нам потрібно викликати сеттер setName.

Виклик сеттера

При виклику Setter (Сеттера) ми оновлюємо значення, яке утримує Signal (Сигнал).

Далі ми перевіряємо список підписників (subscribers), видаляємо їх зі списку і викликаємо кожного.

Ми заміняємо значення всередині Signal (Сигнал) на "Jane", видаляємо Effect (Ефект) зі списку і викликаємо його.

pic

У цьому випадку цей Effect (Ефект):

Це, в свою чергу, виводить "Jane" і повторює процес реєстрації себе як підписника оригінального сигналу.

pic

Ми маємо реактивну систему, яка відповідає на зміни стану.

Легко побачити, як ця система може бути використана для детальних оновлень UI, фактично створюючи Effect (Ефект), який рендерить компонент лише коли змінюється конкретний Signal (Сигнал).

Ми також можемо побачити, як ця система може масштабуватись, коли більше компонентів підписуються на більше Signals (Сигнали).

Усі ці переваги приходять без додаткових зусиль з боку розробника — підписки обробляються автоматично за лаштунками.
Одним словом: неймовірно!

pic

Фото: Nahima Aparicio на Unsplash

Мемоізація (Memoization)

Якщо б я попросив вас знайти добуток 12 * 16, вам, ймовірно, довелося б трохи поміркувати — можливо, подвоїти 6 * 2, а потім додати числа, і так далі.

Але якщо я спитаю вас знову, ви, ймовірно, одразу скажете 192 без зайвих зусиль.

Іноді корисно запам'ятовувати обчислення, які ми вже зробили, замість того, щоб виконувати їх повторно.

Ця логіка стосується нас, і вона також застосовується до Signals (Сигналів).
Ми можемо використовувати функцію createMemo для реалізації цього патерну з Signals (Сигналами).

Що відбувається за лаштунками, коли ми викликаємо функцію createMemo?

pic

По-перше, ми створюємо Memo (Мемо), що містить масив підписників, подібно до того, як ми працюємо з Signals (Сигналами).

Далі ми розміщаємо спеціальну обгорткову функцію M: f(), в контексті відслідковування, так само, як ми робили з Effect (Ефект).

pic

Після цього ми викликаємо функцію, передану як вхід до createMemo.
Коли функція виконується, вона викликає num(), роблячи M: f() підписником (subscriber) сигналу.

pic

Далі ми зберігаємо результат у Memo (Мемо).

pic

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

Давайте подивимося, що відбудеться, коли ми використаємо memo() (значення, яке повертає createMemo).

Ми створили ефект, який викликає функцію memo.

pic

І знову ми бачимо ту саму схему, що й з аксесор-функцією сигналу — коли її викликають, ми перевіряємо верхівку контексту відслідковування та реєструємо Effect (Ефект) як підписника до Memo.

pic

Далі ми виводимо effect: 2, використовуючи мемоізоване значення без повторного обчислення — великий успіх!

Давайте подивимося, як усе змінюється, коли ми змінимо значення сигналу.

pic

Після зміни значення сигналу на 2, ми викликаємо всіх підписників — в цьому випадку обгорткову мемо-функцію.

Повторний виклик функції призводить до наступного:

  • Обгорткова функція memo знову реєструється як підписник сигналу.
  • Значення, збережене в Memo, оновлюється до 4.
  • Ми викликаємо всіх підписників Memo.

pic

Потім, викликавши підписників Memo, ми запускаємо Effect (Ефект) і виводимо effect: 4.

Усі підписники повертаються на свої місця для наступної зміни сигналу :).

pic

По суті, Memo дозволяє нам створювати граф реактивності будь-якого розміру, з покращенням продуктивності завдяки мемоізації важких обчислень.

Не забувайте використовувати createMemo коли хочете уникнути повторного обчислення важких обчислень.

Підсумок

Ми вивчили, як працюють Signals (Сигнали) за лаштунками з їх автоматичним механізмом підписки.

Далі ми вивчили, як працюють Effects (Ефекти) і як вони підписуються на Signals (Сигнали) за допомогою контексту відслідковування.

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

Signals (Сигнали) — це потужний і популярний інструмент у світі фронтенду.
Тепер, коли ви розумієте, як вони працюють, ви можете створити щось неймовірне за їх допомогою.

Посилання

[

Вивчайте реактивне програмування з SolidJS | Курс від творця SolidJS, Раяна Карніато

SolidJS використовує свою реактивність для створення детальних підписок, які оновлюють лише частини DOM при змінах, замість...

frontendmasters.com

](https://frontendmasters.com/courses/reactivity-solidjs/?source=post_page-----19cbcb6b802b--------------------------------)

[

Документація Solid

Solid — це сучасний JavaScript фреймворк, створений для побудови чуйних і високопродуктивних інтерфейсів користувача (UI). Він…

docs.solidjs.com

](https://docs.solidjs.com/?source=post_page-----19cbcb6b802b--------------------------------)

Перекладено з: Signals behind the scenes

Leave a Reply

Your email address will not be published. Required fields are marked *