
Введение
В современных 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 и поставьте 👍 реакцию. Ваша поддержка поможет приоритетизировать эту доработку и принести проверку реактивной чистоты в ваш любимый фреймворк тестирования!
— Удачной разработки без побочных эффектов!