
Введение
В современных Angular-приложениях сигналы предоставляют мощный способ управления реактивными потоками данных. Однако по мере роста сложности приложений становится всё более важным поддерживать чистоту функций, которые взаимодействуют с сигналами. Реактивная чистота гарантирует, что функции не создают нежелательных побочных эффектов, регистрируя новых производителей в реактивном графе.
Проблема
При написании функций, которые читают сигналы, легко — особенно в больших кодовых базах — непреднамеренно ввести побочные эффекты. Например, утилита для обновления сигнала может при определённых условиях зарегистрировать дополнительные реактивные «производители». Эти скрытые побочные эффекты могут привести к утечкам памяти, неожиданным повторным вычислениям или трудно отлавливаемым ошибкам при чтении или записи сигналов. Без проверки того, что функция остаётся «реактивно чистой», разработчикам приходится полагаться на код ревью, что ненадёжно, и отнимает много времени.
Общее решение
Чтобы решить эту задачу на уровне функций — независимо от конкретного фреймворка тестирования — мы можем ввести «проверку реактивной чистоты». Основная идея: выполнить функцию в контролируемом реактивном контексте, который отслеживает любые регистрации производителей (другими словами вызовы сигналов). После выполнения функции нужно проанализировать контекст: если были зарегистрированы производители, значит, функция нарушила реактивную чистоту. Набросок реализации на TypeScript может выглядеть так:
function isReactivePure(fn: () => void): boolean { // Создаём изолированный реактивный контекст const reactiveNode: ReactiveNode = Object.create(REACTIVE_NODE); const prevConsumer = setActiveConsumer(reactiveNode); reactiveNode.consumerAllowSignalWrites = true; try { fn(); // Вызываем переданную функцию } finally { setActiveConsumer(prevConsumer); } // Если в контексте записаны "производители", реактивная чистота функции нарушена return !reactiveNode.producerNode?.length; }
Предложение по интеграции с Jest
Наличие такой проверки уже полезно, но интеграция её в API матчер-ов Jest сделает написание тестов ещё удобнее. Вместо того чтобы вызывать вспомогательную функцию и вручную проверять её логический результат, разработчики смогут писать понятные тесты:
it('не должен иметь побочных эффектов', () => { expect(() => { someMethodThatCallSignals(); }).toBeReactivePure(); });
Здесь toBeReactivePure внутри создаёт реактивный контекст, выполняет переданную функцию и выдаёт подробное сообщение об ошибке, если были зарегистрированы производители. Благодаря такому расширению Jest команды смогут гарантировать реактивную чистоту по всему проекту с минимальным количеством шаблонного кода.
Нужна ваша поддержка
Эта функциональность повысит надёжность реактивного кода в Angular-проектах и за их пределами. Просим вас поддержать инициативу: перейдите к предложению в репозитории jest-preset-angular и поставьте 👍 реакцию. Ваша поддержка поможет приоритетизировать эту доработку и принести проверку реактивной чистоты в ваш любимый фреймворк тестирования!
— Удачной разработки без побочных эффектов!
